Immutable implementation
A ring buffer is a pair of an IndexedSeq
and an Int
pointer into this sequence. I provide code for a immutable version. Note that not all methods that might be useful are implemented; like the mutators that change the content of the IndexedSeq
.
With this implementation, shifting is just creating one new object. So it's pretty efficient.
Example code
class RingBuffer[A](val index: Int, val data: IndexedSeq[A]) extends IndexedSeq[A] {
def shiftLeft = new RingBuffer((index + 1) % data.size, data)
def shiftRight = new RingBuffer((index + data.size - 1) % data.size, data)
def length = data.length
def apply(i: Int) = data((index + i) % data.size)
}
val rb = new RingBuffer(0, IndexedSeq(2,3,5,7,11))
println("plain: " + rb)
println("sl: " + rb.shiftLeft)
println("sr: " + rb.shiftRight)
Output
plain: Main(2, 3, 5, 7, 11)
sl: Main(3, 5, 7, 11, 2)
sr: Main(11, 2, 3, 5, 7)
Performance comparison to mutable implementations
The OP mentions that you should look at the mutable implementations (e.g. this answer), if you need performance. This is not true in general. As always: It depends.
Immutable
- update:
O(log n)
, which is basically the update complexity of the underlying IndexedSeq
;
- shifting:
O(1)
, also involves creating a new object which may cost some cycles
Mutable
- update:
O(1)
, array update, as fast as it gets
- shifting:
O(n)
, you have to touch every element once; fast implementations on primitive arrays might still win against the immutable version for small arrays, because of constant factor
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…