Last active
February 27, 2024 00:46
-
-
Save ToxicBakery/05d3d98256aaae50bfbde04ae0c62dbd to your computer and use it in GitHub Desktop.
Simple circular/ring buffer style implementation backed by an array for kotlin with test coverage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.concurrent.atomic.AtomicInteger | |
class CircularArray<T> : Iterable<T>, Cloneable { | |
/** | |
* Creates a new instance of the array with the given size. | |
*/ | |
constructor(bufferSize: Int) { | |
this.arr = arrayOfNulls(bufferSize) | |
this.tail = -1 | |
} | |
/** | |
* Creates a new instance of the array as a copy. | |
*/ | |
constructor(circularArray: CircularArray<T>) { | |
this.arr = circularArray.arr.copyOf() | |
this._size = circularArray._size | |
this.tail = circularArray.tail | |
} | |
private val arr: Array<Any?> | |
private var _size: Int = 0 | |
private var tail: Int | |
private val head: Int | |
get() = if (_size == arr.size) (tail + 1) % _size else 0 | |
/** | |
* Number of elements currently stored in the array. | |
*/ | |
val size: Int | |
get() = _size | |
/** | |
* Add an element to the array. | |
*/ | |
fun add(item: T) { | |
tail = (tail + 1) % arr.size | |
arr[tail] = item | |
if (_size < arr.size) _size++ | |
} | |
/** | |
* Get an element from the array. | |
*/ | |
@Suppress("UNCHECKED_CAST") | |
operator fun get(index: Int): T = | |
when { | |
_size == 0 || index > _size || index < 0 -> throw IndexOutOfBoundsException("$index") | |
_size == arr.size -> arr[(head + index) % arr.size] | |
else -> arr[index] | |
} as T | |
/** | |
* This array as a list. | |
*/ | |
@Suppress("UNCHECKED_CAST") | |
fun toList(): List<T> = iterator().asSequence().toList() | |
public override fun clone(): CircularArray<T> = CircularArray(this) | |
override fun iterator(): Iterator<T> = object : Iterator<T> { | |
private val index: AtomicInteger = AtomicInteger(0) | |
override fun hasNext(): Boolean = index.get() < size | |
override fun next(): T = get(index.getAndIncrement()) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.Assert.assertEquals | |
import org.junit.Test | |
class CircularArrayTest { | |
@Test | |
fun getSize() { | |
val circularArray = CircularArray<Int>(10) | |
assertEquals(0, circularArray.size) | |
circularArray.add(1) | |
assertEquals(1, circularArray.size) | |
(0..100).forEach(circularArray::add) | |
assertEquals(10, circularArray.size) | |
} | |
@Test | |
fun get() { | |
CircularArray<Int>(1).also { circularArray -> | |
circularArray.add(1) | |
assertEquals(1, circularArray[0]) | |
circularArray.add(2) | |
assertEquals(2, circularArray[0]) | |
} | |
CircularArray<Int>(2).also { circularArray -> | |
circularArray.add(1) | |
assertEquals(1, circularArray[0]) | |
circularArray.add(2) | |
assertEquals(1, circularArray[0]) | |
assertEquals(2, circularArray[1]) | |
circularArray.add(3) | |
assertEquals(2, circularArray[0]) | |
assertEquals(3, circularArray[1]) | |
circularArray.add(4) | |
assertEquals(3, circularArray[0]) | |
assertEquals(4, circularArray[1]) | |
} | |
} | |
@Test(expected = IndexOutOfBoundsException::class) | |
fun getZeroSizeException() { | |
CircularArray<Int>(1).get(0) | |
} | |
@Test(expected = IndexOutOfBoundsException::class) | |
fun getIndexOutOfBounds() { | |
CircularArray<Int>(1).also { circularArray -> | |
circularArray.add(1) | |
circularArray[10] | |
} | |
} | |
@Test(expected = IndexOutOfBoundsException::class) | |
fun getIndexNegative() { | |
CircularArray<Int>(1).also { circularArray -> | |
circularArray.add(1) | |
circularArray[-1] | |
} | |
} | |
@Test | |
fun toList() { | |
CircularArray<Int>(1).also { circularArray -> | |
circularArray.add(0) | |
assertEquals(listOf(0), circularArray.toList()) | |
circularArray.add(1) | |
assertEquals(listOf(1), circularArray.toList()) | |
} | |
CircularArray<Int>(2).also { circularArray -> | |
circularArray.add(0) | |
assertEquals(listOf(0), circularArray.toList()) | |
circularArray.add(1) | |
assertEquals(listOf(0, 1), circularArray.toList()) | |
circularArray.add(2) | |
assertEquals(listOf(1, 2), circularArray.toList()) | |
println("${circularArray[0]}") | |
circularArray.add(3) | |
assertEquals(listOf(2, 3), circularArray.toList()) | |
circularArray.add(4) | |
assertEquals(listOf(3, 4), circularArray.toList()) | |
} | |
} | |
@Test | |
fun testClone() { | |
CircularArray<Int>(1) | |
.also { circularArray -> circularArray.add(1) } | |
.clone() | |
.also { clonedCircularArray -> assertEquals(1, clonedCircularArray[0]) } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment