Common ArrayDeque
This commit is contained in:
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
private val emptyElementData = emptyArray<Any?>()
|
||||
private const val maxArraySize = Int.MAX_VALUE - 8
|
||||
private const val defaultMinCapacity = 10
|
||||
|
||||
/**
|
||||
* Resizable-array implementation of the deque data structure.
|
||||
*
|
||||
* The name deque is short for "double ended queue" and is usually pronounced "deck".
|
||||
*
|
||||
* The collection provide methods for convenient access to the both ends.
|
||||
* It also implements [MutableList] interface and supports efficient get/set operations by index.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalStdlibApi
|
||||
public class ArrayDeque<E> : AbstractMutableList<E> {
|
||||
private var head: Int = 0
|
||||
private var elementData: Array<Any?>
|
||||
|
||||
override var size: Int = 0
|
||||
private set
|
||||
|
||||
/**
|
||||
* Constructs an empty deque with specified [initialCapacity], or throws [IllegalArgumentException] if [initialCapacity] is negative.
|
||||
*/
|
||||
public constructor(initialCapacity: Int) {
|
||||
elementData = when {
|
||||
initialCapacity == 0 -> emptyElementData
|
||||
initialCapacity > 0 -> arrayOfNulls(initialCapacity)
|
||||
else -> throw IllegalArgumentException("Illegal Capacity: $initialCapacity")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an empty deque.
|
||||
*/
|
||||
public constructor() {
|
||||
elementData = emptyElementData
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a deque that contains the same elements as the specified [elements] collection in the same order.
|
||||
*/
|
||||
public constructor(elements: Collection<E>) {
|
||||
elementData = elements.toTypedArray()
|
||||
size = elementData.size
|
||||
if (elementData.isEmpty()) elementData = emptyElementData
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the capacity of this deque is at least equal to the specified [minCapacity].
|
||||
*
|
||||
* If the current capacity is less than the [minCapacity], a new backing storage is allocated with greater capacity.
|
||||
* Otherwise, this method takes no action and simply returns.
|
||||
*/
|
||||
private fun ensureCapacity(minCapacity: Int) {
|
||||
if (minCapacity < 0) throw IllegalStateException("Deque is too big.") // overflow
|
||||
if (minCapacity <= elementData.size) return
|
||||
if (elementData === emptyElementData) {
|
||||
elementData = arrayOfNulls(minCapacity.coerceAtLeast(defaultMinCapacity))
|
||||
return
|
||||
}
|
||||
|
||||
val newCapacity = newCapacity(elementData.size, minCapacity)
|
||||
copyElements(newCapacity)
|
||||
}
|
||||
|
||||
// made internal for testing
|
||||
internal fun newCapacity(oldCapacity: Int, minCapacity: Int): Int {
|
||||
// overflow-conscious
|
||||
var newCapacity = oldCapacity + (oldCapacity shr 1)
|
||||
if (newCapacity - minCapacity < 0)
|
||||
newCapacity = minCapacity
|
||||
if (newCapacity - maxArraySize > 0)
|
||||
newCapacity = if (minCapacity > maxArraySize) Int.MAX_VALUE else maxArraySize
|
||||
return newCapacity
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array with the specified [newCapacity] size and copies elements in the [elementData] array to it.
|
||||
*/
|
||||
private fun copyElements(newCapacity: Int) {
|
||||
val newElements = arrayOfNulls<Any?>(newCapacity)
|
||||
elementData.copyInto(newElements, 0, head, elementData.size)
|
||||
elementData.copyInto(newElements, elementData.size - head, 0, head)
|
||||
head = 0
|
||||
elementData = newElements
|
||||
}
|
||||
|
||||
@kotlin.internal.InlineOnly
|
||||
private inline fun internalGet(internalIndex: Int): E {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return elementData[internalIndex] as E
|
||||
}
|
||||
|
||||
private fun positiveMod(index: Int): Int = if (index >= elementData.size) index - elementData.size else index
|
||||
|
||||
private fun negativeMod(index: Int): Int = if (index < 0) index + elementData.size else index
|
||||
|
||||
@kotlin.internal.InlineOnly
|
||||
private inline fun internalIndex(index: Int): Int = positiveMod(head + index)
|
||||
|
||||
private fun incremented(index: Int): Int = if (index == elementData.lastIndex) 0 else index + 1
|
||||
|
||||
private fun decremented(index: Int): Int = if (index == 0) elementData.lastIndex else index - 1
|
||||
|
||||
override fun isEmpty(): Boolean = size == 0
|
||||
|
||||
/**
|
||||
* Returns the first element, or throws [NoSuchElementException] if this deque is empty.
|
||||
*/
|
||||
public fun first(): E = if (isEmpty()) throw NoSuchElementException("ArrayDeque is empty.") else internalGet(head)
|
||||
|
||||
/**
|
||||
* Returns the first element, or `null` if this deque is empty.
|
||||
*/
|
||||
public fun firstOrNull(): E? = if (isEmpty()) null else internalGet(head)
|
||||
|
||||
/**
|
||||
* Returns the last element, or throws [NoSuchElementException] if this deque is empty.
|
||||
*/
|
||||
public fun last(): E = if (isEmpty()) throw NoSuchElementException("ArrayDeque is empty.") else internalGet(internalIndex(lastIndex))
|
||||
|
||||
/**
|
||||
* Returns the last element, or `null` if this deque is empty.
|
||||
*/
|
||||
public fun lastOrNull(): E? = if (isEmpty()) null else internalGet(internalIndex(lastIndex))
|
||||
|
||||
/**
|
||||
* Prepends the specified [element] to this deque.
|
||||
*/
|
||||
public fun addFirst(element: E) {
|
||||
ensureCapacity(size + 1)
|
||||
|
||||
head = decremented(head)
|
||||
elementData[head] = element
|
||||
size += 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified [element] to this deque.
|
||||
*/
|
||||
public fun addLast(element: E) {
|
||||
ensureCapacity(size + 1)
|
||||
|
||||
elementData[internalIndex(size)] = element
|
||||
size += 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first element from this deque and returns that removed element, or throws [NoSuchElementException] if this deque is empty.
|
||||
*/
|
||||
public fun removeFirst(): E {
|
||||
if (isEmpty()) throw NoSuchElementException("ArrayDeque is empty.")
|
||||
|
||||
val element = internalGet(head)
|
||||
elementData[head] = null
|
||||
head = incremented(head)
|
||||
size -= 1
|
||||
return element
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first element from this deque and returns that removed element, or returns `null` if this deque is empty.
|
||||
*/
|
||||
public fun removeFirstOrNull(): E? = if (isEmpty()) null else removeFirst()
|
||||
|
||||
/**
|
||||
* Removes the last element from this deque and returns that removed element, or throws [NoSuchElementException] if this deque is empty.
|
||||
*/
|
||||
public fun removeLast(): E {
|
||||
if (isEmpty()) throw NoSuchElementException("ArrayDeque is empty.")
|
||||
|
||||
val internalLastIndex = internalIndex(lastIndex)
|
||||
val element = internalGet(internalLastIndex)
|
||||
elementData[internalLastIndex] = null
|
||||
size -= 1
|
||||
return element
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the last element from this deque and returns that removed element, or returns `null` if this deque is empty.
|
||||
*/
|
||||
public fun removeLastOrNull(): E? = if (isEmpty()) null else removeLast()
|
||||
|
||||
// MutableList, MutableCollection
|
||||
public override fun add(element: E): Boolean {
|
||||
addLast(element)
|
||||
return true
|
||||
}
|
||||
|
||||
public override fun add(index: Int, element: E) {
|
||||
AbstractList.checkPositionIndex(index, size)
|
||||
|
||||
if (index == size) {
|
||||
addLast(element)
|
||||
return
|
||||
} else if (index == 0) {
|
||||
addFirst(element)
|
||||
return
|
||||
}
|
||||
|
||||
ensureCapacity(size + 1)
|
||||
|
||||
// Elements in circular array lay in 2 ways:
|
||||
// 1. `head` is less than `tail`: [#, #, e1, e2, e3, #]
|
||||
// 2. `head` is greater than `tail`: [e3, #, #, #, e1, e2]
|
||||
// where head is the index of the first element in the circular array,
|
||||
// and tail is the index following the last element.
|
||||
//
|
||||
// At this point the insertion index is not equal to head or tail.
|
||||
// Also the circular array can store at least one more element.
|
||||
//
|
||||
// Depending on where the given element must be inserted the preceding or the succeeding
|
||||
// elements will be shifted to make room for the element to be inserted.
|
||||
//
|
||||
// In case the preceding elements are shifted:
|
||||
// * if the insertion index is greater than the head (regardless of circular array form)
|
||||
// -> shift the preceding elements
|
||||
// * otherwise, the circular array has (2) form and the insertion index is less than tail
|
||||
// -> shift all elements in the back of the array
|
||||
// -> shift preceding elements in the front of the array
|
||||
// In case the succeeding elements are shifted:
|
||||
// * if the insertion index is less than the tail (regardless of circular array form)
|
||||
// -> shift the succeeding elements
|
||||
// * otherwise, the circular array has (2) form and the insertion index is greater than head
|
||||
// -> shift all elements in the front of the array
|
||||
// -> shift succeeding elements in the back of the array
|
||||
|
||||
val internalIndex = internalIndex(index)
|
||||
|
||||
if (index < (size + 1) shr 1) {
|
||||
// closer to the first element -> shift preceding elements
|
||||
val decrementedInternalIndex = decremented(internalIndex)
|
||||
val decrementedHead = decremented(head)
|
||||
|
||||
if (decrementedInternalIndex >= head) {
|
||||
elementData[decrementedHead] = elementData[head] // head can be zero
|
||||
elementData.copyInto(elementData, head, head + 1, decrementedInternalIndex + 1)
|
||||
} else { // head > tail
|
||||
elementData.copyInto(elementData, head - 1, head, elementData.size) // head can't be zero
|
||||
elementData[elementData.size - 1] = elementData[0]
|
||||
elementData.copyInto(elementData, 0, 1, decrementedInternalIndex + 1)
|
||||
}
|
||||
|
||||
elementData[decrementedInternalIndex] = element
|
||||
head = decrementedHead
|
||||
} else {
|
||||
// closer to the last element -> shift succeeding elements
|
||||
val tail = internalIndex(size)
|
||||
|
||||
if (internalIndex < tail) {
|
||||
elementData.copyInto(elementData, internalIndex + 1, internalIndex, tail)
|
||||
} else { // head > tail
|
||||
elementData.copyInto(elementData, 1, 0, tail)
|
||||
elementData[0] = elementData[elementData.size - 1]
|
||||
elementData.copyInto(elementData, internalIndex + 1, internalIndex, elementData.size - 1)
|
||||
}
|
||||
|
||||
elementData[internalIndex] = element
|
||||
}
|
||||
size += 1
|
||||
}
|
||||
|
||||
private fun copyCollectionElements(internalIndex: Int, elements: Collection<E>) {
|
||||
val iterator = elements.iterator()
|
||||
|
||||
for (index in internalIndex until elementData.size) {
|
||||
if (!iterator.hasNext()) break
|
||||
elementData[index] = iterator.next()
|
||||
}
|
||||
for (index in 0 until head) {
|
||||
if (!iterator.hasNext()) break
|
||||
elementData[index] = iterator.next()
|
||||
}
|
||||
|
||||
size += elements.size
|
||||
}
|
||||
|
||||
public override fun addAll(elements: Collection<E>): Boolean {
|
||||
if (elements.isEmpty()) return false
|
||||
ensureCapacity(this.size + elements.size)
|
||||
copyCollectionElements(internalIndex(size), elements)
|
||||
return true
|
||||
}
|
||||
|
||||
public override fun addAll(index: Int, elements: Collection<E>): Boolean {
|
||||
AbstractList.checkPositionIndex(index, size)
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
return false
|
||||
} else if (index == size) {
|
||||
return addAll(elements)
|
||||
}
|
||||
|
||||
ensureCapacity(this.size + elements.size)
|
||||
|
||||
val tail = internalIndex(size)
|
||||
val internalIndex = internalIndex(index)
|
||||
val elementsSize = elements.size
|
||||
|
||||
if (index < (size + 1) shr 1) {
|
||||
// closer to the first element -> shift preceding elements
|
||||
|
||||
var shiftedHead = head - elementsSize
|
||||
|
||||
if (internalIndex >= head) {
|
||||
if (shiftedHead >= 0) {
|
||||
elementData.copyInto(elementData, shiftedHead, head, internalIndex)
|
||||
} else { // head < tail, insertion leads to head >= tail
|
||||
shiftedHead += elementData.size
|
||||
val elementsToShift = internalIndex - head
|
||||
val shiftToBack = elementData.size - shiftedHead
|
||||
|
||||
if (shiftToBack >= elementsToShift) {
|
||||
elementData.copyInto(elementData, shiftedHead, head, internalIndex)
|
||||
} else {
|
||||
elementData.copyInto(elementData, shiftedHead, head, head + shiftToBack)
|
||||
elementData.copyInto(elementData, 0, head + shiftToBack, internalIndex)
|
||||
}
|
||||
}
|
||||
} else { // head > tail, internalIndex < tail
|
||||
elementData.copyInto(elementData, shiftedHead, head, elementData.size)
|
||||
if (elementsSize >= internalIndex) {
|
||||
elementData.copyInto(elementData, elementData.size - elementsSize, 0, internalIndex)
|
||||
} else {
|
||||
elementData.copyInto(elementData, elementData.size - elementsSize, 0, elementsSize)
|
||||
elementData.copyInto(elementData, 0, elementsSize, internalIndex)
|
||||
}
|
||||
}
|
||||
head = shiftedHead
|
||||
copyCollectionElements(negativeMod(internalIndex - elementsSize), elements)
|
||||
} else {
|
||||
// closer to the last element -> shift succeeding elements
|
||||
|
||||
val shiftedInternalIndex = internalIndex + elementsSize
|
||||
|
||||
if (internalIndex < tail) {
|
||||
if (tail + elementsSize <= elementData.size) {
|
||||
elementData.copyInto(elementData, shiftedInternalIndex, internalIndex, tail)
|
||||
} else { // head < tail, insertion leads to head >= tail
|
||||
if (shiftedInternalIndex >= elementData.size) {
|
||||
elementData.copyInto(elementData, shiftedInternalIndex - elementData.size, internalIndex, tail)
|
||||
} else {
|
||||
val shiftToFront = tail + elementsSize - elementData.size
|
||||
elementData.copyInto(elementData, 0, tail - shiftToFront, tail)
|
||||
elementData.copyInto(elementData, shiftedInternalIndex, internalIndex, tail - shiftToFront)
|
||||
}
|
||||
}
|
||||
} else { // head > tail, internalIndex > head
|
||||
elementData.copyInto(elementData, elementsSize, 0, tail)
|
||||
if (shiftedInternalIndex >= elementData.size) {
|
||||
elementData.copyInto(elementData, shiftedInternalIndex - elementData.size, internalIndex, elementData.size)
|
||||
} else {
|
||||
elementData.copyInto(elementData, 0, elementData.size - elementsSize, elementData.size)
|
||||
elementData.copyInto(elementData, shiftedInternalIndex, internalIndex, elementData.size - elementsSize)
|
||||
}
|
||||
}
|
||||
copyCollectionElements(internalIndex, elements)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public override fun get(index: Int): E {
|
||||
AbstractList.checkElementIndex(index, size)
|
||||
|
||||
return internalGet(internalIndex(index))
|
||||
}
|
||||
|
||||
public override fun set(index: Int, element: E): E {
|
||||
AbstractList.checkElementIndex(index, size)
|
||||
|
||||
val internalIndex = internalIndex(index)
|
||||
val oldElement = internalGet(internalIndex)
|
||||
elementData[internalIndex] = element
|
||||
|
||||
return oldElement
|
||||
}
|
||||
|
||||
public override fun contains(element: E): Boolean = indexOf(element) != -1
|
||||
|
||||
public override fun indexOf(element: E): Int {
|
||||
val tail = internalIndex(size)
|
||||
|
||||
if (head < tail) {
|
||||
for (index in head until tail) {
|
||||
if (element == elementData[index]) return index - head
|
||||
}
|
||||
} else if (head >= tail) {
|
||||
for (index in head until elementData.size) {
|
||||
if (element == elementData[index]) return index - head
|
||||
}
|
||||
for (index in 0 until tail) {
|
||||
if (element == elementData[index]) return index + elementData.size - head
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
public override fun lastIndexOf(element: E): Int {
|
||||
val tail = internalIndex(size)
|
||||
|
||||
if (head < tail) {
|
||||
for (index in tail - 1 downTo head) {
|
||||
if (element == elementData[index]) return index - head
|
||||
}
|
||||
} else if (head > tail) {
|
||||
for (index in tail - 1 downTo 0) {
|
||||
if (element == elementData[index]) return index + elementData.size - head
|
||||
}
|
||||
for (index in elementData.lastIndex downTo head) {
|
||||
if (element == elementData[index]) return index - head
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
public override fun remove(element: E): Boolean {
|
||||
val index = indexOf(element)
|
||||
if (index == -1) return false
|
||||
removeAt(index)
|
||||
return true
|
||||
}
|
||||
|
||||
public override fun removeAt(index: Int): E {
|
||||
AbstractList.checkElementIndex(index, size)
|
||||
|
||||
if (index == lastIndex) {
|
||||
return removeLast()
|
||||
} else if (index == 0) {
|
||||
return removeFirst()
|
||||
}
|
||||
|
||||
val internalIndex = internalIndex(index)
|
||||
val element = internalGet(internalIndex)
|
||||
|
||||
if (index < size shr 1) {
|
||||
// closer to the first element -> shift preceding elements
|
||||
if (internalIndex >= head) {
|
||||
elementData.copyInto(elementData, head + 1, head, internalIndex)
|
||||
} else { // head > tail, internalIndex < head
|
||||
elementData.copyInto(elementData, 1, 0, internalIndex)
|
||||
elementData[0] = elementData[elementData.size - 1]
|
||||
elementData.copyInto(elementData, head + 1, head, elementData.size - 1)
|
||||
}
|
||||
|
||||
elementData[head] = null
|
||||
head = incremented(head)
|
||||
} else {
|
||||
// closer to the last element -> shift succeeding elements
|
||||
val internalLastIndex = internalIndex(lastIndex)
|
||||
|
||||
if (internalIndex <= internalLastIndex) {
|
||||
elementData.copyInto(elementData, internalIndex, internalIndex + 1, internalLastIndex + 1)
|
||||
} else { // head > tail, internalIndex > head
|
||||
elementData.copyInto(elementData, internalIndex, internalIndex + 1, elementData.size)
|
||||
elementData[elementData.size - 1] = elementData[0]
|
||||
elementData.copyInto(elementData, 0, 1, internalLastIndex + 1)
|
||||
}
|
||||
|
||||
elementData[internalLastIndex] = null
|
||||
}
|
||||
size -= 1
|
||||
|
||||
return element
|
||||
}
|
||||
|
||||
public override fun removeAll(elements: Collection<E>): Boolean = filterInPlace { !elements.contains(it) }
|
||||
|
||||
public override fun retainAll(elements: Collection<E>): Boolean = filterInPlace { elements.contains(it) }
|
||||
|
||||
private inline fun filterInPlace(predicate: (E) -> Boolean): Boolean {
|
||||
if (this.isEmpty() || elementData.isEmpty())
|
||||
return false
|
||||
|
||||
val tail = internalIndex(size)
|
||||
var newTail = head
|
||||
var modified = false
|
||||
|
||||
if (head < tail) {
|
||||
for (index in head until tail) {
|
||||
val element = elementData[index]
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (predicate(element as E))
|
||||
elementData[newTail++] = element
|
||||
else
|
||||
modified = true
|
||||
}
|
||||
|
||||
elementData.fill(null, newTail, tail)
|
||||
|
||||
} else {
|
||||
for (index in head until elementData.size) {
|
||||
val element = elementData[index]
|
||||
elementData[index] = null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (predicate(element as E))
|
||||
elementData[newTail++] = element
|
||||
else
|
||||
modified = true
|
||||
}
|
||||
|
||||
newTail = positiveMod(newTail)
|
||||
|
||||
for (index in 0 until tail) {
|
||||
val element = elementData[index]
|
||||
elementData[index] = null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (predicate(element as E)) {
|
||||
elementData[newTail] = element
|
||||
newTail = incremented(newTail)
|
||||
} else {
|
||||
modified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modified)
|
||||
size = negativeMod(newTail - head)
|
||||
|
||||
return modified
|
||||
}
|
||||
|
||||
public override fun clear() {
|
||||
val tail = internalIndex(size)
|
||||
if (head < tail) {
|
||||
elementData.fill(null, head, tail)
|
||||
} else if (isNotEmpty()) {
|
||||
elementData.fill(null, head, elementData.size)
|
||||
elementData.fill(null, 0, tail)
|
||||
}
|
||||
head = 0
|
||||
size = 0
|
||||
}
|
||||
|
||||
// For testing only
|
||||
internal fun internalStructure(structure: (head: Int, elements: Array<Any?>) -> Unit) {
|
||||
val tail = internalIndex(size)
|
||||
|
||||
if (isEmpty()) {
|
||||
structure(head, emptyArray())
|
||||
} else if (head < tail) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
structure(head, elementData.sliceArray(head until tail))
|
||||
} else {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
structure(head - elementData.size, elementData.sliceArray(head until elementData.size).plus(elements = elementData.sliceArray(0 until tail)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package collections
|
||||
|
||||
import test.collections.behaviors.iteratorBehavior
|
||||
import test.collections.behaviors.listIteratorBehavior
|
||||
import test.collections.behaviors.listIteratorProperties
|
||||
import test.collections.compare
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextInt
|
||||
import kotlin.test.*
|
||||
|
||||
class ArrayDequeTest {
|
||||
@Test
|
||||
fun arrayDequeInit() {
|
||||
listOf(ArrayDeque<Int>(), ArrayDeque(10)).forEach { deque ->
|
||||
assertTrue(deque.isEmpty())
|
||||
assertNull(deque.firstOrNull())
|
||||
assertNull(deque.lastOrNull())
|
||||
assertNull(deque.removeFirstOrNull())
|
||||
assertNull(deque.removeLastOrNull())
|
||||
}
|
||||
|
||||
ArrayDeque(listOf(0, 1, 2, 3, 4)).let { deque ->
|
||||
assertFalse(deque.isEmpty())
|
||||
assertEquals(0, deque.firstOrNull())
|
||||
assertEquals(4, deque.lastOrNull())
|
||||
assertEquals(0, deque.removeFirstOrNull())
|
||||
assertEquals(4, deque.removeLastOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun size() {
|
||||
listOf(ArrayDeque<Int>(), ArrayDeque(10)).forEach { deque ->
|
||||
// head == tail
|
||||
assertEquals(0, deque.size)
|
||||
// head > tail
|
||||
deque.addFirst(-1)
|
||||
assertEquals(1, deque.size)
|
||||
// buffer expansion, head < tail
|
||||
deque.addAll(listOf(1, 2, 3, 4, 5, 6, 7))
|
||||
assertEquals(8, deque.size)
|
||||
}
|
||||
|
||||
ArrayDeque(listOf(0, 1, 2, 3, 4)).let { deque ->
|
||||
// head < tail
|
||||
assertEquals(5, deque.size)
|
||||
// head > tail
|
||||
deque.addFirst(-1)
|
||||
assertEquals(6, deque.size)
|
||||
// buffer expansion, head < tail
|
||||
deque.addAll(listOf(5, 6, 7))
|
||||
assertEquals(9, deque.size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun contains() {
|
||||
val originalItems = listOf(0, 1, 2, 3, 4)
|
||||
val extraItems = listOf(-1, 5)
|
||||
val deque = ArrayDeque(originalItems)
|
||||
// head < tail
|
||||
originalItems.forEach { assertTrue(deque.contains(it), "Expected $it in $deque") }
|
||||
extraItems.forEach { assertFalse(deque.contains(it), "Not expected $it in $deque") }
|
||||
|
||||
// head > tail
|
||||
deque.addFirst(-1)
|
||||
deque.addLast(5)
|
||||
|
||||
(originalItems + extraItems).forEach { assertTrue(deque.contains(it), "Expected $it in $deque") }
|
||||
|
||||
// remove, head > tail
|
||||
deque.remove(2)
|
||||
|
||||
assertTrue(deque.contains(1))
|
||||
assertFalse(deque.contains(2))
|
||||
assertTrue(deque.contains(3))
|
||||
|
||||
// remove, head < tail
|
||||
deque.remove(-1)
|
||||
|
||||
assertTrue(deque.contains(5))
|
||||
assertFalse(deque.contains(-1))
|
||||
assertTrue(deque.contains(0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clear() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
val deque = generateArrayDeque(head, tail, bufferSize).apply { clear() }
|
||||
assertTrue(deque.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeElement() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
deque.addLast(0)
|
||||
deque.addLast(1)
|
||||
deque.addLast(2)
|
||||
deque.addLast(3)
|
||||
assertTrue(deque.contains(2))
|
||||
deque.remove(2)
|
||||
assertFalse(deque.contains(2))
|
||||
|
||||
deque.addFirst(-1)
|
||||
assertTrue(deque.containsAll(listOf(-1, 0, 1, 3)))
|
||||
deque.remove(-1)
|
||||
assertTrue(deque.containsAll(listOf(0, 1, 3)))
|
||||
deque.remove(0)
|
||||
assertTrue(deque.containsAll(listOf(1, 3)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun iterator() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addLast(1)
|
||||
deque.addLast(2)
|
||||
deque.addLast(3)
|
||||
compare(deque.iterator(), listOf(0, 1, 2, 3).iterator()) { iteratorBehavior() }
|
||||
|
||||
deque.addFirst(-1)
|
||||
compare(deque.iterator(), listOf(-1, 0, 1, 2, 3).iterator()) { iteratorBehavior() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun first() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
assertFailsWith<NoSuchElementException> { deque.first() }
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addLast(1)
|
||||
assertEquals(0, deque.first())
|
||||
|
||||
deque.addFirst(-1)
|
||||
assertEquals(-1, deque.first())
|
||||
|
||||
deque.removeFirst()
|
||||
assertEquals(0, deque.first())
|
||||
|
||||
deque.clear()
|
||||
assertFailsWith<NoSuchElementException> { deque.first() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun firstOrNull() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
assertNull(deque.firstOrNull())
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addLast(1)
|
||||
assertEquals(0, deque.firstOrNull())
|
||||
|
||||
deque.addFirst(-1)
|
||||
assertEquals(-1, deque.firstOrNull())
|
||||
|
||||
deque.removeFirst()
|
||||
assertEquals(0, deque.firstOrNull())
|
||||
|
||||
deque.clear()
|
||||
assertNull(deque.firstOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun last() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
assertFailsWith<NoSuchElementException> { deque.last() }
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addLast(1)
|
||||
assertEquals(1, deque.last())
|
||||
|
||||
deque.addFirst(-1)
|
||||
assertEquals(1, deque.last())
|
||||
|
||||
deque.removeLast()
|
||||
assertEquals(0, deque.last())
|
||||
|
||||
deque.clear()
|
||||
assertFailsWith<NoSuchElementException> { deque.last() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lastOrNull() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
assertNull(deque.lastOrNull())
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addLast(1)
|
||||
assertEquals(1, deque.lastOrNull())
|
||||
|
||||
deque.addFirst(-1)
|
||||
assertEquals(1, deque.lastOrNull())
|
||||
|
||||
deque.removeLast()
|
||||
assertEquals(0, deque.lastOrNull())
|
||||
|
||||
deque.clear()
|
||||
assertNull(deque.lastOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addFirst() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
|
||||
// head > tail
|
||||
listOf(-1, -2, -3).forEach {
|
||||
deque.addFirst(it)
|
||||
assertEquals(it, deque.first())
|
||||
}
|
||||
|
||||
// head < tail
|
||||
listOf(-1, -2).forEach {
|
||||
assertEquals(it, deque.removeLast())
|
||||
}
|
||||
|
||||
listOf(-4, -5, -6, -7, -8, -9).forEach {
|
||||
deque.addFirst(it)
|
||||
assertEquals(it, deque.first())
|
||||
}
|
||||
|
||||
// buffer expansion, head < tail
|
||||
deque.addFirst(-10)
|
||||
assertEquals(-10, deque.first())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addLast() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
|
||||
// head < tail
|
||||
listOf(0, 1, 2).forEach {
|
||||
deque.addLast(it)
|
||||
assertEquals(it, deque.last())
|
||||
}
|
||||
|
||||
// head > tail
|
||||
listOf(0, 1).forEach {
|
||||
assertEquals(it, deque.removeFirst())
|
||||
}
|
||||
listOf(3, 4, 5, 6, 7, 8).forEach {
|
||||
deque.addLast(it)
|
||||
assertEquals(it, deque.last())
|
||||
}
|
||||
|
||||
// buffer expansion, head < tail
|
||||
deque.addLast(9)
|
||||
assertEquals(9, deque.last())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeFirst() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
assertFailsWith<NoSuchElementException> { deque.removeFirst() }
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addFirst(-1)
|
||||
deque.addFirst(-2)
|
||||
deque.addLast(1)
|
||||
|
||||
assertEquals(-2, deque.removeFirst())
|
||||
assertEquals(-1, deque.removeFirst())
|
||||
assertEquals(0, deque.removeFirst())
|
||||
assertEquals(1, deque.removeFirst())
|
||||
|
||||
assertFailsWith<NoSuchElementException> { deque.removeFirst() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeFirstOrNull() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
assertNull(deque.removeFirstOrNull())
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addFirst(-1)
|
||||
deque.addFirst(-2)
|
||||
deque.addLast(1)
|
||||
|
||||
assertEquals(-2, deque.removeFirstOrNull())
|
||||
assertEquals(-1, deque.removeFirstOrNull())
|
||||
assertEquals(0, deque.removeFirstOrNull())
|
||||
assertEquals(1, deque.removeFirstOrNull())
|
||||
|
||||
assertNull(deque.removeFirstOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeLast() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
assertFailsWith<NoSuchElementException> { deque.removeLast() }
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addFirst(-1)
|
||||
deque.addFirst(-2)
|
||||
deque.addLast(1)
|
||||
|
||||
assertEquals(1, deque.removeLast())
|
||||
assertEquals(0, deque.removeLast())
|
||||
assertEquals(-1, deque.removeLast())
|
||||
assertEquals(-2, deque.removeLast())
|
||||
|
||||
assertFailsWith<NoSuchElementException> { deque.removeLast() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeLastOrNull() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
assertNull(deque.removeLastOrNull())
|
||||
|
||||
deque.addLast(0)
|
||||
deque.addFirst(-1)
|
||||
deque.addFirst(-2)
|
||||
deque.addLast(1)
|
||||
|
||||
assertEquals(1, deque.removeLastOrNull())
|
||||
assertEquals(0, deque.removeLastOrNull())
|
||||
assertEquals(-1, deque.removeLastOrNull())
|
||||
assertEquals(-2, deque.removeLastOrNull())
|
||||
|
||||
assertNull(deque.removeLastOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bufferExpansion() {
|
||||
val deque = ArrayDeque<Int>()
|
||||
|
||||
deque.addAll(listOf(0, 1, 2, 3, 4, 5, 6, 7))
|
||||
assertEquals(listOf(0, 1, 2, 3, 4, 5, 6, 7), deque.toList())
|
||||
|
||||
listOf(-1, -2, -3, -4, -5, -6, -7, -8).forEach { deque.addFirst(it) }
|
||||
assertEquals(listOf(-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7), deque.toList())
|
||||
}
|
||||
|
||||
private fun generateArrayDeque(head: Int, tail: Int, bufferSize: Int? = null): ArrayDeque<Int> {
|
||||
check(tail >= 0)
|
||||
|
||||
val deque = if (bufferSize != null) ArrayDeque<Int>(bufferSize) else ArrayDeque()
|
||||
|
||||
repeat(tail) {
|
||||
deque.addLast(it)
|
||||
if (it < head) deque.removeFirst()
|
||||
}
|
||||
repeat(-head) { deque.addFirst(-(it + 1)) }
|
||||
|
||||
check(tail - head == deque.size)
|
||||
|
||||
return deque
|
||||
}
|
||||
|
||||
private fun testArrayDeque(test: (bufferSize: Int, dequeSize: Int, head: Int, tail: Int) -> Unit) {
|
||||
for (bufferSize in listOf(0, 1, 2, 5, 8, 16)) {
|
||||
for (dequeSize in 0..bufferSize) {
|
||||
for (tail in 0 until bufferSize) {
|
||||
val head = tail - dequeSize
|
||||
test(bufferSize, dequeSize, head, tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MutableList operations
|
||||
@Test
|
||||
fun insert() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
for (index in 0..dequeSize) {
|
||||
val deque = generateArrayDeque(head, tail, bufferSize).apply { add(index, 100) }
|
||||
|
||||
val expectedHead = when {
|
||||
dequeSize == bufferSize -> // buffer expansion
|
||||
when {
|
||||
index * 2 < dequeSize -> -1 // shift preceding elements
|
||||
// shift succeeding elements
|
||||
dequeSize + 1 >= bufferSize + bufferSize / 2 -> -(dequeSize + 1) // tail == 0 -> head becomes negative (head > tail)
|
||||
else -> 0
|
||||
}
|
||||
index * 2 < dequeSize -> head - 1 // shift preceding elements
|
||||
else -> // shift succeeding last elements
|
||||
if (tail == bufferSize - 1)
|
||||
head - bufferSize // tail == 0 -> head becomes negative (head > tail)
|
||||
else
|
||||
head
|
||||
}
|
||||
val expectedElements = (head until tail).toMutableList().apply { add(index, 100) }
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER")
|
||||
deque.internalStructure { actualHead, actualElements ->
|
||||
assertEquals(expectedHead, actualHead, "bufferSize: $bufferSize, head: $head, tail: $tail, index: $index")
|
||||
assertEquals(expectedElements, actualElements.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeAt() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
for (index in 0 until dequeSize) {
|
||||
val deque = generateArrayDeque(head, tail, bufferSize).apply { removeAt(index) }
|
||||
|
||||
val expectedHead = when {
|
||||
index < dequeSize / 2 -> head + 1 // shift preceding elements
|
||||
else -> // shift succeeding elements
|
||||
if (tail == 0)
|
||||
head + bufferSize // tail == bufferSize - 1 -> head becomes positive(head < tail)
|
||||
else
|
||||
head
|
||||
}
|
||||
val expectedElements = (head until tail).toMutableList().apply { removeAt(index) }
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER")
|
||||
deque.internalStructure { actualHead, actualElements ->
|
||||
assertEquals(expectedHead, actualHead, "bufferSize: $bufferSize, head: $head, tail: $tail, index: $index")
|
||||
assertEquals(expectedElements, actualElements.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun indexOf() {
|
||||
// head < tail
|
||||
generateArrayDeque(0, 7).let { deque ->
|
||||
(0..6).forEach { assertEquals(it, deque.indexOf(it)) }
|
||||
assertEquals(-1, deque.indexOf(100))
|
||||
}
|
||||
|
||||
// head > tail
|
||||
generateArrayDeque(-4, 3).let { deque ->
|
||||
(0..6).forEach { assertEquals(it, deque.indexOf(it - 4)) }
|
||||
assertEquals(-1, deque.indexOf(100))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addAll() {
|
||||
// head < tail
|
||||
generateArrayDeque(0, 3).let { deque ->
|
||||
deque.addAll(listOf(3, 4, 5))
|
||||
assertEquals(listOf(0, 1, 2, 3, 4, 5), deque.toList())
|
||||
|
||||
deque.addAll(6..100)
|
||||
assertEquals((0..100).toList(), deque.toList())
|
||||
}
|
||||
|
||||
generateArrayDeque(4, 6).let { deque ->
|
||||
deque.addAll(listOf(6, 7, 8))
|
||||
assertEquals(listOf(4, 5, 6, 7, 8), deque.toList())
|
||||
|
||||
deque.addAll(9..100)
|
||||
assertEquals((4..100).toList(), deque.toList())
|
||||
}
|
||||
|
||||
// head > tail
|
||||
generateArrayDeque(-3, 2).let { deque ->
|
||||
deque.addAll(listOf(2, 3))
|
||||
assertEquals(listOf(-3, -2, -1, 0, 1, 2, 3), deque.toList())
|
||||
|
||||
deque.addAll(4..100)
|
||||
assertEquals((-3..100).toList(), deque.toList())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun insertAll() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
for (insertCollectionSize in 0..bufferSize) {
|
||||
val listToInsert = (0 until insertCollectionSize).map { 100 + it }
|
||||
|
||||
for (index in 0..dequeSize) {
|
||||
val deque = generateArrayDeque(head, tail, bufferSize).apply { addAll(index, listToInsert) }
|
||||
|
||||
val expectedHead = when {
|
||||
dequeSize + listToInsert.size > bufferSize -> // buffer expansion
|
||||
when {
|
||||
index * 2 < dequeSize -> -listToInsert.size // shift preceding elements
|
||||
// shift succeeding elements
|
||||
dequeSize + listToInsert.size >= bufferSize + bufferSize / 2 -> -(dequeSize + listToInsert.size) // tail == 0 -> head becomes negative (head > tail)
|
||||
else -> 0
|
||||
}
|
||||
index * 2 < dequeSize -> head - listToInsert.size // shift preceding elements
|
||||
else -> // shift succeeding elements
|
||||
if (tail + listToInsert.size >= bufferSize)
|
||||
head - bufferSize // tail >= 0 -> head becomes negative (head > tail)
|
||||
else
|
||||
head
|
||||
}
|
||||
val expectedElements = (head until tail).toMutableList().apply { addAll(index, listToInsert) }
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER")
|
||||
deque.internalStructure { actualHead, actualElements ->
|
||||
assertEquals(expectedHead, actualHead, "bufferSize: $bufferSize, head: $head, tail: $tail, index: $index")
|
||||
assertEquals(expectedElements, actualElements.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listIterator() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
|
||||
for (index in 0..dequeSize) {
|
||||
val expectedIterator = (head until tail).toMutableList().listIterator(index)
|
||||
val actualIterator = generateArrayDeque(head, tail, bufferSize).listIterator(index)
|
||||
|
||||
compare(expectedIterator, actualIterator) { listIteratorBehavior() }
|
||||
}
|
||||
|
||||
for (index in 0..dequeSize) {
|
||||
val expectedIterator = (head until tail).toMutableList().listIterator(index)
|
||||
val actualIterator = generateArrayDeque(head, tail, bufferSize).listIterator(index)
|
||||
|
||||
for (element in listOf(100, 101)) {
|
||||
expectedIterator.add(element)
|
||||
actualIterator.add(element)
|
||||
compare(expectedIterator, actualIterator) { listIteratorProperties() }
|
||||
}
|
||||
}
|
||||
|
||||
for (index in 0..dequeSize) {
|
||||
val expectedIterator = (head until tail).toMutableList().listIterator(index)
|
||||
val actualIterator = generateArrayDeque(head, tail, bufferSize).listIterator(index)
|
||||
|
||||
repeat(times = 2) {
|
||||
if (expectedIterator.hasNext()) {
|
||||
expectedIterator.next()
|
||||
actualIterator.next()
|
||||
} else if (expectedIterator.hasPrevious()) {
|
||||
expectedIterator.previous()
|
||||
actualIterator.previous()
|
||||
}
|
||||
if (dequeSize > it) {
|
||||
expectedIterator.remove()
|
||||
actualIterator.remove()
|
||||
}
|
||||
compare(expectedIterator, actualIterator) { listIteratorProperties() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeAll() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
generateArrayDeque(head, tail, bufferSize).let { deque ->
|
||||
deque.removeAll(emptyList())
|
||||
assertEquals((head until tail).toList(), deque)
|
||||
|
||||
val absentElements = listOf(head - 1, tail, Random.nextInt(tail..Int.MAX_VALUE))
|
||||
deque.removeAll(absentElements)
|
||||
assertEquals((head until tail).toList(), deque)
|
||||
|
||||
deque.removeAll((head until tail).toList())
|
||||
assertTrue(deque.isEmpty())
|
||||
}
|
||||
|
||||
val listToRemove = (head - 1 until tail + 1).filter { Random.nextBoolean() }
|
||||
|
||||
val elements = (head until tail).toMutableList().apply { removeAll(listToRemove) }
|
||||
val deque = generateArrayDeque(head, tail, bufferSize).apply { removeAll(listToRemove) }
|
||||
|
||||
assertEquals(elements, deque)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun retainAll() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
val listToRetain = (head..tail).filter { Random.nextBoolean() }
|
||||
|
||||
val elements = (head until tail).toMutableList().apply { retainAll(listToRetain) }
|
||||
val deque = generateArrayDeque(head, tail, bufferSize).apply { retainAll(listToRetain) }
|
||||
|
||||
assertEquals(elements, deque)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun set() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
for (index in 0 until dequeSize) {
|
||||
val elements = (head until tail).toMutableList()
|
||||
val deque = generateArrayDeque(head, tail, bufferSize)
|
||||
|
||||
elements[index] = 100
|
||||
deque[index] = 100
|
||||
|
||||
assertEquals(elements, deque)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun get() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
val elements = (head until tail).toList()
|
||||
val deque = generateArrayDeque(head, tail, bufferSize)
|
||||
|
||||
for (index in 0 until dequeSize) {
|
||||
assertEquals(elements[index], deque[index])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun subList() = testArrayDeque { bufferSize: Int, dequeSize: Int, head: Int, tail: Int ->
|
||||
val elements = (head until tail).toList()
|
||||
val deque = generateArrayDeque(head, tail, bufferSize)
|
||||
|
||||
for (fromIndex in 0 until dequeSize) {
|
||||
for (toIndex in fromIndex..dequeSize) {
|
||||
assertEquals(elements.subList(fromIndex, toIndex), deque.subList(fromIndex, toIndex))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER")
|
||||
@Test
|
||||
fun newCapacity() {
|
||||
val deque = ArrayDeque<String>()
|
||||
|
||||
// oldCapacity < minCapacity < newCapacity
|
||||
repeat(100) {
|
||||
val oldCapacity = Random.nextInt(1 shl 30)
|
||||
val newCapacity = oldCapacity + (oldCapacity shr 1)
|
||||
val minCapacity = Random.nextInt(oldCapacity + 1 until newCapacity)
|
||||
|
||||
assertEquals(newCapacity, deque.newCapacity(oldCapacity, minCapacity))
|
||||
}
|
||||
|
||||
// oldCapacity < newCapacity < minCapacity
|
||||
repeat(100) {
|
||||
val oldCapacity = Random.nextInt(1 shl 30)
|
||||
val newCapacity = oldCapacity + (oldCapacity shr 1)
|
||||
val minCapacity = Random.nextInt(newCapacity..Int.MAX_VALUE)
|
||||
|
||||
assertEquals(minCapacity, deque.newCapacity(oldCapacity, minCapacity))
|
||||
}
|
||||
|
||||
// newCapacity overflow, oldCapacity < minCapacity <= maxArraySize
|
||||
val maxArraySize = Int.MAX_VALUE - 8
|
||||
repeat(100) {
|
||||
val oldCapacity = Random.nextInt((1 shl 30) + (1 shl 29) until maxArraySize)
|
||||
val minCapacity = Random.nextInt(oldCapacity..maxArraySize)
|
||||
|
||||
assertEquals(maxArraySize, deque.newCapacity(oldCapacity, minCapacity))
|
||||
}
|
||||
|
||||
// newCapacity overflow, minCapacity > maxArraySize
|
||||
repeat(100) {
|
||||
val oldCapacity = Random.nextInt((1 shl 30) + (1 shl 29)..maxArraySize)
|
||||
val minCapacity = Random.nextInt(maxArraySize + 1..Int.MAX_VALUE)
|
||||
|
||||
assertEquals(Int.MAX_VALUE, deque.newCapacity(oldCapacity, minCapacity))
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -664,6 +664,38 @@ public abstract class kotlin/collections/AbstractSet : kotlin/collections/Abstra
|
||||
public fun iterator ()Ljava/util/Iterator;
|
||||
}
|
||||
|
||||
public final class kotlin/collections/ArrayDeque : kotlin/collections/AbstractMutableList {
|
||||
public fun <init> ()V
|
||||
public fun <init> (I)V
|
||||
public fun <init> (Ljava/util/Collection;)V
|
||||
public fun add (ILjava/lang/Object;)V
|
||||
public fun add (Ljava/lang/Object;)Z
|
||||
public fun addAll (ILjava/util/Collection;)Z
|
||||
public fun addAll (Ljava/util/Collection;)Z
|
||||
public final fun addFirst (Ljava/lang/Object;)V
|
||||
public final fun addLast (Ljava/lang/Object;)V
|
||||
public fun clear ()V
|
||||
public fun contains (Ljava/lang/Object;)Z
|
||||
public final fun first ()Ljava/lang/Object;
|
||||
public final fun firstOrNull ()Ljava/lang/Object;
|
||||
public fun get (I)Ljava/lang/Object;
|
||||
public fun getSize ()I
|
||||
public fun indexOf (Ljava/lang/Object;)I
|
||||
public fun isEmpty ()Z
|
||||
public final fun last ()Ljava/lang/Object;
|
||||
public fun lastIndexOf (Ljava/lang/Object;)I
|
||||
public final fun lastOrNull ()Ljava/lang/Object;
|
||||
public fun remove (Ljava/lang/Object;)Z
|
||||
public fun removeAll (Ljava/util/Collection;)Z
|
||||
public fun removeAt (I)Ljava/lang/Object;
|
||||
public final fun removeFirst ()Ljava/lang/Object;
|
||||
public final fun removeFirstOrNull ()Ljava/lang/Object;
|
||||
public final fun removeLast ()Ljava/lang/Object;
|
||||
public final fun removeLastOrNull ()Ljava/lang/Object;
|
||||
public fun retainAll (Ljava/util/Collection;)Z
|
||||
public fun set (ILjava/lang/Object;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class kotlin/collections/ArraysKt {
|
||||
public static final fun all ([BLkotlin/jvm/functions/Function1;)Z
|
||||
public static final fun all ([CLkotlin/jvm/functions/Function1;)Z
|
||||
|
||||
Reference in New Issue
Block a user