diff --git a/libraries/stdlib/src/kotlin/ImmutableArrayList.kt b/libraries/stdlib/src/kotlin/ImmutableArrayList.kt new file mode 100644 index 00000000000..90027286ee8 --- /dev/null +++ b/libraries/stdlib/src/kotlin/ImmutableArrayList.kt @@ -0,0 +1,87 @@ +package kotlin + +import java.util.List +import java.util.AbstractList + +private class ImmutableArrayList( + private val array: Array, + private val offset: Int, + private val length: Int +) : AbstractList() { + { + // impossible + if (offset < 0) { + throw IllegalArgumentException("negative offset") + } + // impossible + if (length < 0) { + throw IllegalArgumentException("negative length") + } + // possible when builder is used from different threads + if (offset + length > array.size) { + throw IllegalArgumentException("offset + length > array.length") + } + } + + protected fun indexInArray(index: Int): Int { + if (index < 0) { + throw IllegalArgumentException("negative index") + } + if (index >= length) { + throw IllegalArgumentException("index > length") + } + return index + offset + } + + public override fun get(index: Int): T = array[indexInArray(index)] as T + + public override fun size() : Int = length + + public override fun subList(fromIndex: Int, toIndex: Int) : List { + if (fromIndex < 0) { + throw IllegalArgumentException("negative from index") + } + if (toIndex < fromIndex) { + throw IllegalArgumentException("toIndex < fromIndex") + } + if (toIndex > length) { + throw IllegalArgumentException("fromIndex + toIndex > length") + } + return ImmutableArrayList(array, offset + fromIndex, toIndex - fromIndex) + } + + // TODO: efficiently implement iterator and other stuff +} + +// TODO: make val, see http://youtrack.jetbrains.com/issue/KT-2028 +private fun emptyArray(): Array = arrayOfNulls(0) + +public class ImmutableArrayListBuilder() { + + private var array = emptyArray() + private var length = 0 + + public fun build(): List { + val r = ImmutableArrayList(array as Array, 0, length) + array = emptyArray() + length = 0 + return r + } + + public fun ensureCapacity(capacity: Int) { + if (array.size < capacity) { + val newSize = Math.max(capacity, Math.max(array.size * 2, 11)) + array = array.copyOf(newSize) + } + } + + public fun add(item: T) { + ensureCapacity(length + 1) + array[length] = item + ++length + } + +} + +// default list builder +fun listBuilder() = ImmutableArrayListBuilder() diff --git a/libraries/stdlib/test/ImmutableArrayListTest.kt b/libraries/stdlib/test/ImmutableArrayListTest.kt new file mode 100644 index 00000000000..d3ec1058160 --- /dev/null +++ b/libraries/stdlib/test/ImmutableArrayListTest.kt @@ -0,0 +1,63 @@ +package test.collection + +import kotlin.test.* + +import junit.framework.TestCase +import java.util.List +import java.util.Random + +class ImmutableArrayListTest() : TestCase() { + + fun testSimple() { + val builder = ImmutableArrayListBuilder() + builder.add(17) + val list = builder.build() + assertEquals(1, list.size()) + assertEquals(17, list[0]) + } + + + fun testGet() { + for (length in 0 .. 55) { + val list = buildIntArray(length, 19) + assertEquals(length, list.size) + checkList(list, length, 19) + } + } + + private fun buildIntArray(length: Int, firstValue: Int): List { + val builder = ImmutableArrayListBuilder() + for (j in 0 .. length - 1) { + builder.add(firstValue + j) + } + return builder.build() + } + + + private fun checkList(list: List, expectedLength: Int, expectedFirstValue: Int) { + assertEquals(expectedLength, list.size) + for (i in 0 .. expectedLength - 1) { + assertEquals(expectedFirstValue + i, list[i]) + } + try { + list[expectedLength] + fail() + } catch (e: IllegalArgumentException) { + // expected + } + } + + + fun testSublist() { + val r = Random(1) + for (i in 0 .. 200) { + val length = r.nextInt(55) + val list = buildIntArray(length, 23) + val fromIndex = r.nextInt(length + 1) + val toIndex = fromIndex + r.nextInt(length - fromIndex + 1) + val sublist = list.subList(fromIndex, toIndex) + checkList(sublist, toIndex - fromIndex, 23 + fromIndex) + } + } + +}