Simplify K/N ArrayList

This commit specializes the existing Kotlin/Native stdlib ArrayList into
two subclasses:
 * ArrayList
 * ArraySubList
This avoids repeatedly checking whether a basic ArrayList is created as a
sublist of another ArrayList.

In the iterators, checkForComodification is marked for inlining, since
this significantly improves iterations performance.

A number of benchmarks are added to the native ring benchmark suite, to
test whether the changed runtime type of ArrayList.subList(...) has an
impact.
This commit is contained in:
Troels Bjerre Lund
2023-08-29 13:54:49 +02:00
committed by Space Cloud
parent 39fda3535a
commit d62dbbb1bd
3 changed files with 510 additions and 115 deletions
@@ -218,6 +218,15 @@ class RingLauncher : Launcher() {
"ParameterNotNull.invokeEightArgsWithNullCheck" to BenchmarkEntryWithInit.create(::ParameterNotNullAssertionBenchmark, { invokeEightArgsWithNullCheck() }),
"ParameterNotNull.invokeEightArgsWithoutNullCheck" to BenchmarkEntryWithInit.create(::ParameterNotNullAssertionBenchmark, { invokeEightArgsWithoutNullCheck() }),
"String.stringConcatNullable" to BenchmarkEntryWithInit.create(::StringBenchmark, { stringConcatNullable() }),
"SubList.concatenate" to BenchmarkEntryWithInit.create(::SubListBenchmark, { concatenate() }),
"SubList.concatenateManual" to BenchmarkEntryWithInit.create(::SubListBenchmark, { concatenateManual() }),
"SubList.filterAndCount" to BenchmarkEntryWithInit.create(::SubListBenchmark, { filterAndCount() }),
"SubList.filterAndCountWithLambda" to BenchmarkEntryWithInit.create(::SubListBenchmark, { filterAndCountWithLambda() }),
"SubList.countWithLambda" to BenchmarkEntryWithInit.create(::SubListBenchmark, { countWithLambda() }),
"SubList.filterManual" to BenchmarkEntryWithInit.create(::SubListBenchmark, { filterManual() }),
"SubList.countFilteredManual" to BenchmarkEntryWithInit.create(::SubListBenchmark, { countFilteredManual() }),
"SubList.countFiltered" to BenchmarkEntryWithInit.create(::SubListBenchmark, { countFiltered() }),
"SubList.reduce" to BenchmarkEntryWithInit.create(::SubListBenchmark, { reduce() }),
"Switch.testSparseIntSwitch" to BenchmarkEntryWithInit.create(::SwitchBenchmark, { testSparseIntSwitch() }),
"Switch.testDenseIntSwitch" to BenchmarkEntryWithInit.create(::SwitchBenchmark, { testDenseIntSwitch() }),
"Switch.testConstSwitch" to BenchmarkEntryWithInit.create(::SwitchBenchmark, { testConstSwitch() }),
@@ -0,0 +1,121 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.ring
class SubListBenchmark {
private var _data: List<Value>? = null
fun getData(subList: Boolean): List<Value> {
val data: List<Value> = _data!!
// 1 to ensure that .subList can't return the list itself
if (subList) return data.subList(1, data.size)
else return data
}
init {
val list = ArrayList<Value>(BENCHMARK_SIZE)
for (n in classValues(BENCHMARK_SIZE))
list.add(n)
_data = list
}
//Benchmark
fun concatenate(): List<Value> {
return getData(false) + getData(true)
}
//Benchmark
fun concatenateManual(): List<Value> {
val list = ArrayList<Value>(2 * BENCHMARK_SIZE)
// outer loop to ensure single call site for list and sublist
for (data in listOf(getData(false), getData(true))) {
for (item in data) {
list.add(item)
}
}
return list
}
//Benchmark
fun filterAndCount(): Int {
var count = 0
for (data in listOf(getData(false), getData(true))) {
count += data.filter { filterLoad(it) }.count()
}
return count
}
//Benchmark
fun filterAndCountWithLambda(): Int {
var count = 0
for (data in listOf(getData(false), getData(true))) {
count += data.filter { it.value % 2 == 0 }.count()
}
return count
}
//Benchmark
fun countWithLambda(): Int {
var count = 0
for (data in listOf(getData(false), getData(true))) {
count += data.count { it.value % 2 == 0 }
}
return count
}
//Benchmark
fun filterManual(): List<Value> {
val list = ArrayList<Value>()
for (data in listOf(getData(false), getData(true))) {
for (it in data) {
if (filterLoad(it))
list.add(it)
}
}
return list
}
//Benchmark
fun countFilteredManual(): Int {
var count = 0
for (data in listOf(getData(false), getData(true))) {
for (it in data) {
if (filterLoad(it))
count++
}
}
return count
}
//Benchmark
fun countFiltered(): Int {
var count = 0
for (data in listOf(getData(false), getData(true))) {
count += data.count { filterLoad(it) }
}
return count
}
//Benchmark
fun reduce(): Int {
var res = 0
for (data in listOf(getData(false), getData(true))) {
res = data.fold(res) { acc, it -> if (filterLoad(it)) acc + 1 else acc }
}
return res
}
}
@@ -7,20 +7,13 @@ package kotlin.collections
actual class ArrayList<E> private constructor(
private var backingArray: Array<E>,
private var offset: Int,
private var length: Int,
private var isReadOnly: Boolean,
private val backingList: ArrayList<E>?,
private val root: ArrayList<E>?
private var isReadOnly: Boolean
) : MutableList<E>, RandomAccess, AbstractMutableList<E>() {
private companion object {
private val Empty = ArrayList<Nothing>(0).also { it.isReadOnly = true }
}
init {
if (backingList != null) this.modCount = backingList.modCount
}
/**
* Creates a new empty [ArrayList].
*/
@@ -39,7 +32,7 @@ actual class ArrayList<E> private constructor(
* @throws IllegalArgumentException if [initialCapacity] is negative.
*/
actual constructor(initialCapacity: Int) : this(
arrayOfUninitializedElements(initialCapacity), 0, 0, false, null, null)
arrayOfUninitializedElements(initialCapacity), 0, false)
/**
* Creates a new [ArrayList] filled with the elements of the specified collection.
@@ -52,7 +45,6 @@ actual class ArrayList<E> private constructor(
@PublishedApi
internal fun build(): List<E> {
if (backingList != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList
checkIsMutable()
isReadOnly = true
return if (length > 0) this else Empty
@@ -60,45 +52,39 @@ actual class ArrayList<E> private constructor(
override actual val size: Int
get() {
checkForComodification()
return length
}
override actual fun isEmpty(): Boolean {
checkForComodification()
return length == 0
}
override actual fun get(index: Int): E {
checkForComodification()
AbstractList.checkElementIndex(index, length)
return backingArray[offset + index]
return backingArray[index]
}
override actual operator fun set(index: Int, element: E): E {
checkIsMutable()
checkForComodification()
AbstractList.checkElementIndex(index, length)
val old = backingArray[offset + index]
backingArray[offset + index] = element
val old = backingArray[index]
backingArray[index] = element
return old
}
override actual fun indexOf(element: E): Int {
checkForComodification()
var i = 0
while (i < length) {
if (backingArray[offset + i] == element) return i
if (backingArray[i] == element) return i
i++
}
return -1
}
override actual fun lastIndexOf(element: E): Int {
checkForComodification()
var i = length - 1
while (i >= 0) {
if (backingArray[offset + i] == element) return i
if (backingArray[i] == element) return i
i--
}
return -1
@@ -108,58 +94,50 @@ actual class ArrayList<E> private constructor(
override actual fun listIterator(): MutableListIterator<E> = listIterator(0)
override actual fun listIterator(index: Int): MutableListIterator<E> {
checkForComodification()
AbstractList.checkPositionIndex(index, length)
return Itr(this, index)
}
override actual fun add(element: E): Boolean {
checkIsMutable()
checkForComodification()
addAtInternal(offset + length, element)
addAtInternal(length, element)
return true
}
override actual fun add(index: Int, element: E) {
checkIsMutable()
checkForComodification()
AbstractList.checkPositionIndex(index, length)
addAtInternal(offset + index, element)
addAtInternal(index, element)
}
override actual fun addAll(elements: Collection<E>): Boolean {
checkIsMutable()
checkForComodification()
val n = elements.size
addAllInternal(offset + length, elements, n)
addAllInternal(length, elements, n)
return n > 0
}
override actual fun addAll(index: Int, elements: Collection<E>): Boolean {
checkIsMutable()
checkForComodification()
AbstractList.checkPositionIndex(index, length)
val n = elements.size
addAllInternal(offset + index, elements, n)
addAllInternal(index, elements, n)
return n > 0
}
override actual fun clear() {
checkIsMutable()
checkForComodification()
removeRangeInternal(offset, length)
removeRangeInternal(0, length)
}
override actual fun removeAt(index: Int): E {
checkIsMutable()
checkForComodification()
AbstractList.checkElementIndex(index, length)
return removeAtInternal(offset + index)
return removeAtInternal(index)
}
override actual fun remove(element: E): Boolean {
checkIsMutable()
checkForComodification()
val i = indexOf(element)
if (i >= 0) removeAt(i)
return i >= 0
@@ -167,67 +145,58 @@ actual class ArrayList<E> private constructor(
override actual fun removeAll(elements: Collection<E>): Boolean {
checkIsMutable()
checkForComodification()
return retainOrRemoveAllInternal(offset, length, elements, false) > 0
return retainOrRemoveAllInternal(0, length, elements, false) > 0
}
override actual fun retainAll(elements: Collection<E>): Boolean {
checkIsMutable()
checkForComodification()
return retainOrRemoveAllInternal(offset, length, elements, true) > 0
return retainOrRemoveAllInternal(0, length, elements, true) > 0
}
override actual fun subList(fromIndex: Int, toIndex: Int): MutableList<E> {
AbstractList.checkRangeIndexes(fromIndex, toIndex, length)
return ArrayList(backingArray, offset + fromIndex, toIndex - fromIndex, isReadOnly, this, root ?: this)
return ArraySubList(backingArray, fromIndex, toIndex - fromIndex, null, this)
}
actual fun trimToSize() {
if (backingList != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList
registerModification()
if (length < backingArray.size)
backingArray = backingArray.copyOfUninitializedElements(length)
}
final actual fun ensureCapacity(minCapacity: Int) {
if (backingList != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList
if (minCapacity <= backingArray.size) return
registerModification()
ensureCapacityInternal(minCapacity)
}
override fun equals(other: Any?): Boolean {
checkForComodification()
return other === this ||
(other is List<*>) && contentEquals(other)
}
override fun hashCode(): Int {
checkForComodification()
return backingArray.subarrayContentHashCode(offset, length)
return backingArray.subarrayContentHashCode(0, length)
}
override fun toString(): String {
checkForComodification()
return backingArray.subarrayContentToString(offset, length, this)
return backingArray.subarrayContentToString(0, length, this)
}
@Suppress("UNCHECKED_CAST")
override fun <T> toArray(array: Array<T>): Array<T> {
checkForComodification()
if (array.size < length) {
return backingArray.copyOfRange(fromIndex = offset, toIndex = offset + length) as Array<T>
return backingArray.copyOfRange(fromIndex = 0, toIndex = length) as Array<T>
}
(backingArray as Array<T>).copyInto(array, 0, startIndex = offset, endIndex = offset + length)
(backingArray as Array<T>).copyInto(array, 0, startIndex = 0, endIndex = length)
return terminateCollectionToArray(length, array)
}
override fun toArray(): Array<Any?> {
checkForComodification()
@Suppress("UNCHECKED_CAST")
return backingArray.copyOfRange(fromIndex = offset, toIndex = offset + length) as Array<Any?>
return backingArray.copyOfRange(fromIndex = 0, toIndex = length) as Array<Any?>
}
// ---------------------------- private ----------------------------
@@ -236,13 +205,8 @@ actual class ArrayList<E> private constructor(
modCount += 1
}
private fun checkForComodification() {
if (root != null && root.modCount != modCount)
throw ConcurrentModificationException()
}
private fun checkIsMutable() {
if (isReadOnly || root != null && root.isReadOnly) throw UnsupportedOperationException()
if (isReadOnly) throw UnsupportedOperationException()
}
private fun ensureExtraCapacity(n: Int) {
@@ -258,89 +222,62 @@ actual class ArrayList<E> private constructor(
}
private fun contentEquals(other: List<*>): Boolean {
return backingArray.subarrayContentEquals(offset, length, other)
return backingArray.subarrayContentEquals(0, length, other)
}
private fun insertAtInternal(i: Int, n: Int) {
ensureExtraCapacity(n)
backingArray.copyInto(backingArray, startIndex = i, endIndex = offset + length, destinationOffset = i + n)
backingArray.copyInto(backingArray, startIndex = i, endIndex = length, destinationOffset = i + n)
length += n
}
private fun addAtInternal(i: Int, element: E) {
registerModification()
if (backingList != null) {
backingList.addAtInternal(i, element)
backingArray = backingList.backingArray
length++
} else {
insertAtInternal(i, 1)
backingArray[i] = element
}
insertAtInternal(i, 1)
backingArray[i] = element
}
private fun addAllInternal(i: Int, elements: Collection<E>, n: Int) {
registerModification()
if (backingList != null) {
backingList.addAllInternal(i, elements, n)
backingArray = backingList.backingArray
length += n
} else {
insertAtInternal(i, n)
var j = 0
val it = elements.iterator()
while (j < n) {
backingArray[i + j] = it.next()
j++
}
insertAtInternal(i, n)
var j = 0
val it = elements.iterator()
while (j < n) {
backingArray[i + j] = it.next()
j++
}
}
private fun removeAtInternal(i: Int): E {
registerModification()
if (backingList != null) {
val old = backingList.removeAtInternal(i)
length--
return old
} else {
val old = backingArray[i]
backingArray.copyInto(backingArray, startIndex = i + 1, endIndex = offset + length, destinationOffset = i)
backingArray.resetAt(offset + length - 1)
length--
return old
}
val old = backingArray[i]
backingArray.copyInto(backingArray, startIndex = i + 1, endIndex = length, destinationOffset = i)
backingArray.resetAt(length - 1)
length--
return old
}
private fun removeRangeInternal(rangeOffset: Int, rangeLength: Int) {
if (rangeLength > 0) registerModification()
if (backingList != null) {
backingList.removeRangeInternal(rangeOffset, rangeLength)
} else {
backingArray.copyInto(backingArray, startIndex = rangeOffset + rangeLength, endIndex = length, destinationOffset = rangeOffset)
backingArray.resetRange(fromIndex = length - rangeLength, toIndex = length)
}
backingArray.copyInto(backingArray, startIndex = rangeOffset + rangeLength, endIndex = length, destinationOffset = rangeOffset)
backingArray.resetRange(fromIndex = length - rangeLength, toIndex = length)
length -= rangeLength
}
/** Retains elements if [retain] == true and removes them it [retain] == false. */
private fun retainOrRemoveAllInternal(rangeOffset: Int, rangeLength: Int, elements: Collection<E>, retain: Boolean): Int {
val removed = if (backingList != null) {
backingList.retainOrRemoveAllInternal(rangeOffset, rangeLength, elements, retain)
} else {
var i = 0
var j = 0
while (i < rangeLength) {
if (elements.contains(backingArray[rangeOffset + i]) == retain) {
backingArray[rangeOffset + j++] = backingArray[rangeOffset + i++]
} else {
i++
}
var i = 0
var j = 0
while (i < rangeLength) {
if (elements.contains(backingArray[rangeOffset + i]) == retain) {
backingArray[rangeOffset + j++] = backingArray[rangeOffset + i++]
} else {
i++
}
val removed = rangeLength - j
backingArray.copyInto(backingArray, startIndex = rangeOffset + rangeLength, endIndex = length, destinationOffset = rangeOffset + j)
backingArray.resetRange(fromIndex = length - removed, toIndex = length)
removed
}
val removed = rangeLength - j
backingArray.copyInto(backingArray, startIndex = rangeOffset + rangeLength, endIndex = length, destinationOffset = rangeOffset + j)
backingArray.resetRange(fromIndex = length - removed, toIndex = length)
if (removed > 0) registerModification()
length -= removed
return removed
@@ -369,14 +306,14 @@ actual class ArrayList<E> private constructor(
checkForComodification()
if (index <= 0) throw NoSuchElementException()
lastIndex = --index
return list.backingArray[list.offset + lastIndex]
return list.backingArray[lastIndex]
}
override fun next(): E {
checkForComodification()
if (index >= list.length) throw NoSuchElementException()
lastIndex = index++
return list.backingArray[list.offset + lastIndex]
return list.backingArray[lastIndex]
}
override fun set(element: E) {
@@ -401,11 +338,339 @@ actual class ArrayList<E> private constructor(
expectedModCount = list.modCount
}
private fun checkForComodification() {
// Must inline for native, suppress warning for WASM
@Suppress("NOTHING_TO_INLINE")
private inline fun checkForComodification() {
if (list.modCount != expectedModCount)
throw ConcurrentModificationException()
}
}
private class ArraySubList<E>(
private var backingArray: Array<E>,
private val offset: Int,
private var length: Int,
private val backingList: ArraySubList<E>?,
private val root: ArrayList<E>
) : MutableList<E>, RandomAccess, AbstractMutableList<E>() {
init {
this.modCount = root.modCount
}
override val size: Int
get() {
checkForComodification()
return length
}
override fun isEmpty(): Boolean {
checkForComodification()
return length == 0
}
override fun get(index: Int): E {
checkForComodification()
AbstractList.checkElementIndex(index, length)
return backingArray[offset + index]
}
override operator fun set(index: Int, element: E): E {
checkIsMutable()
checkForComodification()
AbstractList.checkElementIndex(index, length)
val old = backingArray[offset + index]
backingArray[offset + index] = element
return old
}
override fun indexOf(element: E): Int {
checkForComodification()
var i = 0
while (i < length) {
if (backingArray[offset + i] == element) return i
i++
}
return -1
}
override fun lastIndexOf(element: E): Int {
checkForComodification()
var i = length - 1
while (i >= 0) {
if (backingArray[offset + i] == element) return i
i--
}
return -1
}
override fun iterator(): MutableIterator<E> = listIterator(0)
override fun listIterator(): MutableListIterator<E> = listIterator(0)
override fun listIterator(index: Int): MutableListIterator<E> {
checkForComodification()
AbstractList.checkPositionIndex(index, length)
return Itr(this, index)
}
override fun add(element: E): Boolean {
checkIsMutable()
checkForComodification()
addAtInternal(offset + length, element)
return true
}
override fun add(index: Int, element: E) {
checkIsMutable()
checkForComodification()
AbstractList.checkPositionIndex(index, length)
addAtInternal(offset + index, element)
}
override fun addAll(elements: Collection<E>): Boolean {
checkIsMutable()
checkForComodification()
val n = elements.size
addAllInternal(offset + length, elements, n)
return n > 0
}
override fun addAll(index: Int, elements: Collection<E>): Boolean {
checkIsMutable()
checkForComodification()
AbstractList.checkPositionIndex(index, length)
val n = elements.size
addAllInternal(offset + index, elements, n)
return n > 0
}
override fun clear() {
checkIsMutable()
checkForComodification()
removeRangeInternal(offset, length)
}
override fun removeAt(index: Int): E {
checkIsMutable()
checkForComodification()
AbstractList.checkElementIndex(index, length)
return removeAtInternal(offset + index)
}
override fun remove(element: E): Boolean {
checkIsMutable()
checkForComodification()
val i = indexOf(element)
if (i >= 0) removeAt(i)
return i >= 0
}
override fun removeAll(elements: Collection<E>): Boolean {
checkIsMutable()
checkForComodification()
return retainOrRemoveAllInternal(offset, length, elements, false) > 0
}
override fun retainAll(elements: Collection<E>): Boolean {
checkIsMutable()
checkForComodification()
return retainOrRemoveAllInternal(offset, length, elements, true) > 0
}
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E> {
AbstractList.checkRangeIndexes(fromIndex, toIndex, length)
return ArraySubList(backingArray, offset + fromIndex, toIndex - fromIndex, this, root)
}
override fun equals(other: Any?): Boolean {
checkForComodification()
return other === this ||
(other is List<*>) && contentEquals(other)
}
override fun hashCode(): Int {
checkForComodification()
return backingArray.subarrayContentHashCode(offset, length)
}
override fun toString(): String {
checkForComodification()
return backingArray.subarrayContentToString(offset, length, this)
}
@Suppress("UNCHECKED_CAST")
override fun <T> toArray(array: Array<T>): Array<T> {
checkForComodification()
if (array.size < length) {
return backingArray.copyOfRange(fromIndex = offset, toIndex = offset + length) as Array<T>
}
(backingArray as Array<T>).copyInto(array, 0, startIndex = offset, endIndex = offset + length)
return terminateCollectionToArray(length, array)
}
override fun toArray(): Array<Any?> {
checkForComodification()
@Suppress("UNCHECKED_CAST")
return backingArray.copyOfRange(fromIndex = offset, toIndex = offset + length) as Array<Any?>
}
// ---------------------------- private ----------------------------
private fun registerModification() {
modCount += 1
}
private fun checkForComodification() {
if (root.modCount != modCount)
throw ConcurrentModificationException()
}
private fun checkIsMutable() {
if (root.isReadOnly) throw UnsupportedOperationException()
}
private fun ensureExtraCapacity(n: Int) {
ensureCapacityInternal(length + n)
}
private fun ensureCapacityInternal(minCapacity: Int) {
if (minCapacity < 0) throw OutOfMemoryError() // overflow
if (minCapacity > backingArray.size) {
val newSize = AbstractList.newCapacity(backingArray.size, minCapacity)
backingArray = backingArray.copyOfUninitializedElements(newSize)
}
}
private fun contentEquals(other: List<*>): Boolean {
return backingArray.subarrayContentEquals(offset, length, other)
}
private fun addAtInternal(i: Int, element: E) {
registerModification()
val backingList = backingList
if (backingList != null) {
backingList.addAtInternal(i, element)
} else {
root.addAtInternal(i, element)
}
backingArray = root.backingArray
length++
}
private fun addAllInternal(i: Int, elements: Collection<E>, n: Int) {
registerModification()
val backingList = backingList
if (backingList != null) {
backingList.addAllInternal(i, elements, n)
} else {
root.addAllInternal(i, elements, n)
}
backingArray = root.backingArray
length += n
}
private fun removeAtInternal(i: Int): E {
registerModification()
val backingList = backingList
val old = if (backingList != null) {
backingList.removeAtInternal(i)
} else {
root.removeAtInternal(i)
}
length--
return old
}
private fun removeRangeInternal(rangeOffset: Int, rangeLength: Int) {
if (rangeLength > 0) registerModification()
val backingList = backingList
if (backingList != null) {
backingList.removeRangeInternal(rangeOffset, rangeLength)
} else {
root.removeRangeInternal(rangeOffset, rangeLength)
}
length -= rangeLength
}
/** Retains elements if [retain] == true and removes them it [retain] == false. */
private fun retainOrRemoveAllInternal(rangeOffset: Int, rangeLength: Int, elements: Collection<E>, retain: Boolean): Int {
val backingList = backingList
val removed =
if (backingList != null) {
backingList.retainOrRemoveAllInternal(rangeOffset, rangeLength, elements, retain)
} else {
root.retainOrRemoveAllInternal(rangeOffset, rangeLength, elements, retain)
}
if (removed > 0) registerModification()
length -= removed
return removed
}
private class Itr<E> : MutableListIterator<E> {
private val list: ArraySubList<E>
private var index: Int
private var lastIndex: Int
private var expectedModCount: Int
constructor(list: ArraySubList<E>, index: Int) {
this.list = list
this.index = index
this.lastIndex = -1
this.expectedModCount = list.modCount
}
override fun hasPrevious(): Boolean = index > 0
override fun hasNext(): Boolean = index < list.length
override fun previousIndex(): Int = index - 1
override fun nextIndex(): Int = index
override fun previous(): E {
checkForComodification()
if (index <= 0) throw NoSuchElementException()
lastIndex = --index
return list.backingArray[list.offset + lastIndex]
}
override fun next(): E {
checkForComodification()
if (index >= list.length) throw NoSuchElementException()
lastIndex = index++
return list.backingArray[list.offset + lastIndex]
}
override fun set(element: E) {
checkForComodification()
check(lastIndex != -1) { "Call next() or previous() before replacing element from the iterator." }
list.set(lastIndex, element)
}
override fun add(element: E) {
checkForComodification()
list.add(index++, element)
lastIndex = -1
expectedModCount = list.modCount
}
override fun remove() {
checkForComodification()
check(lastIndex != -1) { "Call next() or previous() before removing element from the iterator." }
list.removeAt(lastIndex)
index = lastIndex
lastIndex = -1
expectedModCount = list.modCount
}
// Must inline for native, suppress warning for WASM
@Suppress("NOTHING_TO_INLINE")
private inline fun checkForComodification() {
if (list.modCount != expectedModCount)
throw ConcurrentModificationException()
}
}
}
}
private fun <T> Array<T>.subarrayContentHashCode(offset: Int, length: Int): Int {