[K/N] Catch concurrent modifications of ArrayList
As a part of efforts to stabilize Native stdlib.
This commit is contained in:
committed by
Space Team
parent
c53b49d7fa
commit
9d8d9d000f
@@ -18,6 +18,16 @@ package kotlin.collections
|
||||
* @param E the type of elements contained in the list. The list is invariant in its element type.
|
||||
*/
|
||||
public actual abstract class AbstractMutableList<E> protected actual constructor() : AbstractMutableCollection<E>(), MutableList<E> {
|
||||
/**
|
||||
* The number of times this list is structurally modified.
|
||||
*
|
||||
* A modification is considered to be structural if it changes the list size,
|
||||
* or otherwise changes it in a way that iterations in progress may return incorrect results.
|
||||
*
|
||||
* This value can be used by iterators returned by [iterator] and [listIterator]
|
||||
* to provide fail-fast behavoir when a concurrent modification is detected during iteration.
|
||||
* [ConcurrentModificationException] will be thrown in this case.
|
||||
*/
|
||||
protected var modCount: Int = 0
|
||||
|
||||
abstract override fun add(index: Int, element: E): Unit
|
||||
@@ -91,26 +101,41 @@ public actual abstract class AbstractMutableList<E> protected actual constructor
|
||||
private open inner class IteratorImpl : MutableIterator<E> {
|
||||
/** the index of the item that will be returned on the next call to [next]`()` */
|
||||
protected var index = 0
|
||||
|
||||
/** the index of the item that was returned on the previous call to [next]`()`
|
||||
* or [ListIterator.previous]`()` (for `ListIterator`),
|
||||
* -1 if no such item exists
|
||||
*/
|
||||
protected var last = -1
|
||||
|
||||
/**
|
||||
* The [modCount] value that the backing list is expected to have.
|
||||
* Otherwise, the iterator has detected concurrent modification.
|
||||
*/
|
||||
protected var expectedModCount = modCount
|
||||
|
||||
override fun hasNext(): Boolean = index < size
|
||||
|
||||
override fun next(): E {
|
||||
checkForComodification()
|
||||
if (!hasNext()) throw NoSuchElementException()
|
||||
last = index++
|
||||
return get(last)
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
checkForComodification()
|
||||
check(last != -1) { "Call next() or previous() before removing element from the iterator."}
|
||||
|
||||
removeAt(last)
|
||||
index = last
|
||||
last = -1
|
||||
expectedModCount = modCount
|
||||
}
|
||||
|
||||
protected fun checkForComodification() {
|
||||
if (modCount != expectedModCount)
|
||||
throw ConcurrentModificationException()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +154,7 @@ public actual abstract class AbstractMutableList<E> protected actual constructor
|
||||
override fun nextIndex(): Int = index
|
||||
|
||||
override fun previous(): E {
|
||||
checkForComodification()
|
||||
if (!hasPrevious()) throw NoSuchElementException()
|
||||
|
||||
last = --index
|
||||
@@ -138,14 +164,18 @@ public actual abstract class AbstractMutableList<E> protected actual constructor
|
||||
override fun previousIndex(): Int = index - 1
|
||||
|
||||
override fun add(element: E) {
|
||||
checkForComodification()
|
||||
add(index, element)
|
||||
index++
|
||||
last = -1
|
||||
expectedModCount = modCount
|
||||
}
|
||||
|
||||
override fun set(element: E) {
|
||||
checkForComodification()
|
||||
check(last != -1) { "Call next() or previous() before updating element value with the iterator."}
|
||||
this@AbstractMutableList[last] = element
|
||||
expectedModCount = modCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +192,7 @@ public actual abstract class AbstractMutableList<E> protected actual constructor
|
||||
|
||||
list.add(fromIndex + index, element)
|
||||
_size++
|
||||
modCount = list.modCount
|
||||
}
|
||||
|
||||
override fun get(index: Int): E {
|
||||
@@ -175,6 +206,7 @@ public actual abstract class AbstractMutableList<E> protected actual constructor
|
||||
|
||||
val result = list.removeAt(fromIndex + index)
|
||||
_size--
|
||||
modCount = list.modCount
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -160,12 +160,14 @@ actual class ArrayList<E> private constructor(
|
||||
|
||||
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
|
||||
registerModification()
|
||||
if (minCapacity <= backingArray.size) return
|
||||
ensureCapacityInternal(minCapacity)
|
||||
}
|
||||
@@ -205,6 +207,10 @@ actual class ArrayList<E> private constructor(
|
||||
|
||||
// ---------------------------- private ----------------------------
|
||||
|
||||
private fun registerModification() {
|
||||
modCount += 1
|
||||
}
|
||||
|
||||
private fun checkIsMutable() {
|
||||
if (isReadOnly || root != null && root.isReadOnly) throw UnsupportedOperationException()
|
||||
}
|
||||
@@ -232,6 +238,7 @@ actual class ArrayList<E> private constructor(
|
||||
}
|
||||
|
||||
private fun addAtInternal(i: Int, element: E) {
|
||||
registerModification()
|
||||
if (backingList != null) {
|
||||
backingList.addAtInternal(i, element)
|
||||
backingArray = backingList.backingArray
|
||||
@@ -243,6 +250,7 @@ actual class ArrayList<E> private constructor(
|
||||
}
|
||||
|
||||
private fun addAllInternal(i: Int, elements: Collection<E>, n: Int) {
|
||||
registerModification()
|
||||
if (backingList != null) {
|
||||
backingList.addAllInternal(i, elements, n)
|
||||
backingArray = backingList.backingArray
|
||||
@@ -259,6 +267,7 @@ actual class ArrayList<E> private constructor(
|
||||
}
|
||||
|
||||
private fun removeAtInternal(i: Int): E {
|
||||
registerModification()
|
||||
if (backingList != null) {
|
||||
val old = backingList.removeAtInternal(i)
|
||||
length--
|
||||
@@ -273,6 +282,7 @@ actual class ArrayList<E> private constructor(
|
||||
}
|
||||
|
||||
private fun removeRangeInternal(rangeOffset: Int, rangeLength: Int) {
|
||||
if (rangeLength > 0) registerModification()
|
||||
if (backingList != null) {
|
||||
backingList.removeRangeInternal(rangeOffset, rangeLength)
|
||||
} else {
|
||||
@@ -284,10 +294,8 @@ actual class ArrayList<E> private constructor(
|
||||
|
||||
/** Retains elements if [retain] == true and removes them it [retain] == false. */
|
||||
private fun retainOrRemoveAllInternal(rangeOffset: Int, rangeLength: Int, elements: Collection<E>, retain: Boolean): Int {
|
||||
if (backingList != null) {
|
||||
val removed = backingList.retainOrRemoveAllInternal(rangeOffset, rangeLength, elements, retain)
|
||||
length -= removed
|
||||
return removed
|
||||
val removed = if (backingList != null) {
|
||||
backingList.retainOrRemoveAllInternal(rangeOffset, rangeLength, elements, retain)
|
||||
} else {
|
||||
var i = 0
|
||||
var j = 0
|
||||
@@ -301,20 +309,24 @@ actual class ArrayList<E> private constructor(
|
||||
val removed = rangeLength - j
|
||||
backingArray.copyInto(backingArray, startIndex = rangeOffset + rangeLength, endIndex = length, destinationOffset = rangeOffset + j)
|
||||
backingArray.resetRange(fromIndex = length - removed, toIndex = length)
|
||||
length -= removed
|
||||
return removed
|
||||
removed
|
||||
}
|
||||
if (removed > 0) registerModification()
|
||||
length -= removed
|
||||
return removed
|
||||
}
|
||||
|
||||
private class Itr<E> : MutableListIterator<E> {
|
||||
private val list: ArrayList<E>
|
||||
private var index: Int
|
||||
private var lastIndex: Int
|
||||
private var expectedModCount: Int
|
||||
|
||||
constructor(list: ArrayList<E>, index: Int) {
|
||||
this.list = list
|
||||
this.index = index
|
||||
this.lastIndex = -1
|
||||
this.expectedModCount = list.modCount
|
||||
}
|
||||
|
||||
override fun hasPrevious(): Boolean = index > 0
|
||||
@@ -324,32 +336,44 @@ actual class ArrayList<E> private constructor(
|
||||
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
|
||||
}
|
||||
|
||||
private fun checkForComodification() {
|
||||
if (list.modCount != expectedModCount)
|
||||
throw ConcurrentModificationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user