Implement collection builders
This commit is contained in:
@@ -5,13 +5,6 @@
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
import kotlin.native.concurrent.SharedImmutable
|
||||
|
||||
@SharedImmutable
|
||||
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.
|
||||
*
|
||||
@@ -74,17 +67,6 @@ public class ArrayDeque<E> : AbstractMutableList<E> {
|
||||
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.
|
||||
*/
|
||||
@@ -547,6 +529,22 @@ public class ArrayDeque<E> : AbstractMutableList<E> {
|
||||
size = 0
|
||||
}
|
||||
|
||||
internal companion object {
|
||||
private val emptyElementData = emptyArray<Any?>()
|
||||
private const val maxArraySize = Int.MAX_VALUE - 8
|
||||
private const val defaultMinCapacity = 10
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// For testing only
|
||||
internal fun internalStructure(structure: (head: Int, elements: Array<Any?>) -> Unit) {
|
||||
val tail = internalIndex(size)
|
||||
|
||||
@@ -11,6 +11,7 @@ package kotlin.collections
|
||||
|
||||
import kotlin.contracts.*
|
||||
import kotlin.random.Random
|
||||
import kotlin.collections.builders.*
|
||||
|
||||
internal object EmptyIterator : ListIterator<Nothing> {
|
||||
override fun hasNext(): Boolean = false
|
||||
@@ -169,7 +170,7 @@ public inline fun <T> MutableList(size: Int, init: (index: Int) -> T): MutableLi
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <E> buildList(@BuilderInference builderAction: MutableList<E>.() -> Unit): List<E> {
|
||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||
return ArrayList<E>().apply(builderAction)
|
||||
return ListBuilder<E>().apply(builderAction).build()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,8 +191,7 @@ public inline fun <E> buildList(@BuilderInference builderAction: MutableList<E>.
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <E> buildList(capacity: Int, @BuilderInference builderAction: MutableList<E>.() -> Unit): List<E> {
|
||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||
checkBuilderCapacity(capacity)
|
||||
return ArrayList<E>(capacity).apply(builderAction)
|
||||
return ListBuilder<E>(capacity).apply(builderAction).build()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
import kotlin.collections.builders.MapBuilder
|
||||
import kotlin.contracts.*
|
||||
|
||||
private object EmptyMap : Map<Any?, Nothing>, Serializable {
|
||||
@@ -139,7 +140,7 @@ public fun <K, V> linkedMapOf(vararg pairs: Pair<K, V>): LinkedHashMap<K, V> = p
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <K, V> buildMap(@BuilderInference builderAction: MutableMap<K, V>.() -> Unit): Map<K, V> {
|
||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||
return LinkedHashMap<K, V>().apply(builderAction)
|
||||
return MapBuilder<K, V>().apply(builderAction).build()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,8 +163,7 @@ public inline fun <K, V> buildMap(@BuilderInference builderAction: MutableMap<K,
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <K, V> buildMap(capacity: Int, @BuilderInference builderAction: MutableMap<K, V>.() -> Unit): Map<K, V> {
|
||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||
checkBuilderCapacity(capacity)
|
||||
return LinkedHashMap<K, V>(mapCapacity(capacity)).apply(builderAction)
|
||||
return MapBuilder<K, V>(capacity).apply(builderAction).build()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
package kotlin.collections
|
||||
|
||||
import kotlin.contracts.*
|
||||
import kotlin.collections.builders.*
|
||||
|
||||
internal object EmptySet : Set<Nothing>, Serializable {
|
||||
private const val serialVersionUID: Long = 3406603774387020532
|
||||
@@ -125,7 +126,7 @@ public fun <T : Any> setOfNotNull(vararg elements: T?): Set<T> {
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <E> buildSet(@BuilderInference builderAction: MutableSet<E>.() -> Unit): Set<E> {
|
||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||
return LinkedHashSet<E>().apply(builderAction)
|
||||
return SetBuilder<E>().apply(builderAction).build()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,8 +149,7 @@ public inline fun <E> buildSet(@BuilderInference builderAction: MutableSet<E>.()
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <E> buildSet(capacity: Int, @BuilderInference builderAction: MutableSet<E>.() -> Unit): Set<E> {
|
||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||
checkBuilderCapacity(capacity)
|
||||
return LinkedHashSet<E>(mapCapacity(capacity)).apply(builderAction)
|
||||
return SetBuilder<E>(capacity).apply(builderAction).build()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.builders
|
||||
|
||||
@PublishedApi
|
||||
internal class ListBuilder<E> private constructor(
|
||||
private var array: Array<E>,
|
||||
private var offset: Int,
|
||||
private var length: Int,
|
||||
private var isReadOnly: Boolean,
|
||||
private val backing: ListBuilder<E>?
|
||||
) : MutableList<E>, RandomAccess, AbstractMutableList<E>() {
|
||||
|
||||
constructor() : this(10)
|
||||
|
||||
constructor(initialCapacity: Int) : this(
|
||||
arrayOfUninitializedElements(initialCapacity), 0, 0, false, null)
|
||||
|
||||
fun build(): List<E> {
|
||||
if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ListBuilder
|
||||
checkIsMutable()
|
||||
isReadOnly = true
|
||||
return this
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = length
|
||||
|
||||
override fun isEmpty(): Boolean = length == 0
|
||||
|
||||
override fun get(index: Int): E {
|
||||
AbstractList.checkElementIndex(index, length)
|
||||
return array[offset + index]
|
||||
}
|
||||
|
||||
override operator fun set(index: Int, element: E): E {
|
||||
checkIsMutable()
|
||||
AbstractList.checkElementIndex(index, length)
|
||||
val old = array[offset + index]
|
||||
array[offset + index] = element
|
||||
return old
|
||||
}
|
||||
|
||||
override fun indexOf(element: E): Int {
|
||||
var i = 0
|
||||
while (i < length) {
|
||||
if (array[offset + i] == element) return i
|
||||
i++
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
override fun lastIndexOf(element: E): Int {
|
||||
var i = length - 1
|
||||
while (i >= 0) {
|
||||
if (array[offset + i] == element) return i
|
||||
i--
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<E> = Itr(this, 0)
|
||||
override fun listIterator(): MutableListIterator<E> = Itr(this, 0)
|
||||
|
||||
override fun listIterator(index: Int): MutableListIterator<E> {
|
||||
AbstractList.checkPositionIndex(index, length)
|
||||
return Itr(this, index)
|
||||
}
|
||||
|
||||
override fun add(element: E): Boolean {
|
||||
checkIsMutable()
|
||||
addAtInternal(offset + length, element)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun add(index: Int, element: E) {
|
||||
checkIsMutable()
|
||||
AbstractList.checkPositionIndex(index, length)
|
||||
addAtInternal(offset + index, element)
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<E>): Boolean {
|
||||
checkIsMutable()
|
||||
val n = elements.size
|
||||
addAllInternal(offset + length, elements, n)
|
||||
return n > 0
|
||||
}
|
||||
|
||||
override fun addAll(index: Int, elements: Collection<E>): Boolean {
|
||||
checkIsMutable()
|
||||
AbstractList.checkPositionIndex(index, length)
|
||||
val n = elements.size
|
||||
addAllInternal(offset + index, elements, n)
|
||||
return n > 0
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
checkIsMutable()
|
||||
removeRangeInternal(offset, length)
|
||||
}
|
||||
|
||||
override fun removeAt(index: Int): E {
|
||||
checkIsMutable()
|
||||
AbstractList.checkElementIndex(index, length)
|
||||
return removeAtInternal(offset + index)
|
||||
}
|
||||
|
||||
override fun remove(element: E): Boolean {
|
||||
checkIsMutable()
|
||||
val i = indexOf(element)
|
||||
if (i >= 0) removeAt(i)
|
||||
return i >= 0
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean {
|
||||
checkIsMutable()
|
||||
return retainOrRemoveAllInternal(offset, length, elements, false) > 0
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<E>): Boolean {
|
||||
checkIsMutable()
|
||||
return retainOrRemoveAllInternal(offset, length, elements, true) > 0
|
||||
}
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E> {
|
||||
AbstractList.checkRangeIndexes(fromIndex, toIndex, length)
|
||||
return ListBuilder(array, offset + fromIndex, toIndex - fromIndex, isReadOnly, this)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun ensureCapacity(minCapacity: Int) {
|
||||
if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ListBuilder
|
||||
if (minCapacity > array.size) {
|
||||
val newSize = ArrayDeque.newCapacity(array.size, minCapacity)
|
||||
array = array.copyOfUninitializedElements(newSize)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other === this ||
|
||||
(other is List<*>) && contentEquals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return array.subarrayContentHashCode(offset, length)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return array.subarrayContentToString(offset, length)
|
||||
}
|
||||
|
||||
// ---------------------------- private ----------------------------
|
||||
|
||||
private fun checkIsMutable() {
|
||||
if (isReadOnly) throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
private fun ensureExtraCapacity(n: Int) {
|
||||
ensureCapacity(length + n)
|
||||
}
|
||||
|
||||
private fun contentEquals(other: List<*>): Boolean {
|
||||
return array.subarrayContentEquals(offset, length, other)
|
||||
}
|
||||
|
||||
private fun insertAtInternal(i: Int, n: Int) {
|
||||
ensureExtraCapacity(n)
|
||||
array.copyInto(array, startIndex = i, endIndex = offset + length, destinationOffset = i + n)
|
||||
length += n
|
||||
}
|
||||
|
||||
private fun addAtInternal(i: Int, element: E) {
|
||||
if (backing != null) {
|
||||
backing.addAtInternal(i, element)
|
||||
array = backing.array
|
||||
length++
|
||||
} else {
|
||||
insertAtInternal(i, 1)
|
||||
array[i] = element
|
||||
}
|
||||
}
|
||||
|
||||
private fun addAllInternal(i: Int, elements: Collection<E>, n: Int) {
|
||||
if (backing != null) {
|
||||
backing.addAllInternal(i, elements, n)
|
||||
array = backing.array
|
||||
length += n
|
||||
} else {
|
||||
insertAtInternal(i, n)
|
||||
var j = 0
|
||||
val it = elements.iterator()
|
||||
while (j < n) {
|
||||
array[i + j] = it.next()
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeAtInternal(i: Int): E {
|
||||
if (backing != null) {
|
||||
val old = backing.removeAtInternal(i)
|
||||
length--
|
||||
return old
|
||||
} else {
|
||||
val old = array[i]
|
||||
array.copyInto(array, startIndex = i + 1, endIndex = offset + length, destinationOffset = i)
|
||||
array.resetAt(offset + length - 1)
|
||||
length--
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeRangeInternal(rangeOffset: Int, rangeLength: Int) {
|
||||
if (backing != null) {
|
||||
backing.removeRangeInternal(rangeOffset, rangeLength)
|
||||
} else {
|
||||
array.copyInto(array, startIndex = rangeOffset + rangeLength, endIndex = length, destinationOffset = rangeOffset)
|
||||
array.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 {
|
||||
if (backing != null) {
|
||||
val removed = backing.retainOrRemoveAllInternal(rangeOffset, rangeLength, elements, retain)
|
||||
length -= removed
|
||||
return removed
|
||||
} else {
|
||||
var i = 0
|
||||
var j = 0
|
||||
while (i < rangeLength) {
|
||||
if (elements.contains(array[rangeOffset + i]) == retain) {
|
||||
array[rangeOffset + j++] = array[rangeOffset + i++]
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
val removed = rangeLength - j
|
||||
array.copyInto(array, startIndex = rangeOffset + rangeLength, endIndex = length, destinationOffset = rangeOffset + j)
|
||||
array.resetRange(fromIndex = length - removed, toIndex = length)
|
||||
length -= removed
|
||||
return removed
|
||||
}
|
||||
}
|
||||
|
||||
private class Itr<E> : MutableListIterator<E> {
|
||||
private val list: ListBuilder<E>
|
||||
private var index: Int
|
||||
private var lastIndex: Int
|
||||
|
||||
constructor(list: ListBuilder<E>, index: Int) {
|
||||
this.list = list
|
||||
this.index = index
|
||||
this.lastIndex = -1
|
||||
}
|
||||
|
||||
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 {
|
||||
if (index <= 0) throw NoSuchElementException()
|
||||
lastIndex = --index
|
||||
return list.array[list.offset + lastIndex]
|
||||
}
|
||||
|
||||
override fun next(): E {
|
||||
if (index >= list.length) throw NoSuchElementException()
|
||||
lastIndex = index++
|
||||
return list.array[list.offset + lastIndex]
|
||||
}
|
||||
|
||||
override fun set(element: E) {
|
||||
list.checkIsMutable()
|
||||
check(lastIndex != -1) { "Call next() or previous() before replacing element from the iterator." }
|
||||
list.array[list.offset + lastIndex] = element
|
||||
}
|
||||
|
||||
override fun add(element: E) {
|
||||
list.add(index++, element)
|
||||
lastIndex = -1
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
check(lastIndex != -1) { "Call next() or previous() before removing element from the iterator." }
|
||||
list.removeAt(lastIndex)
|
||||
index = lastIndex
|
||||
lastIndex = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <E> arrayOfUninitializedElements(size: Int): Array<E> {
|
||||
require(size >= 0) { "capacity must be non-negative." }
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return arrayOfNulls<Any?>(size) as Array<E>
|
||||
}
|
||||
|
||||
private fun <T> Array<out T>.subarrayContentToString(offset: Int, length: Int): String {
|
||||
val sb = StringBuilder(2 + length * 3)
|
||||
sb.append("[")
|
||||
var i = 0
|
||||
while (i < length) {
|
||||
if (i > 0) sb.append(", ")
|
||||
sb.append(this[offset + i])
|
||||
i++
|
||||
}
|
||||
sb.append("]")
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun <T> Array<T>.subarrayContentHashCode(offset: Int, length: Int): Int {
|
||||
var result = 1
|
||||
var i = 0
|
||||
while (i < length) {
|
||||
val nextElement = this[offset + i]
|
||||
result = result * 31 + nextElement.hashCode()
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun <T> Array<T>.subarrayContentEquals(offset: Int, length: Int, other: List<*>): Boolean {
|
||||
if (length != other.size) return false
|
||||
var i = 0
|
||||
while (i < length) {
|
||||
if (this[offset + i] != other[i]) return false
|
||||
i++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun <T> Array<T>.copyOfUninitializedElements(newSize: Int): Array<T> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return copyOf(newSize) as Array<T>
|
||||
}
|
||||
|
||||
internal fun <E> Array<E>.resetAt(index: Int) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(this as Array<Any?>)[index] = null
|
||||
}
|
||||
|
||||
internal fun <E> Array<E>.resetRange(fromIndex: Int, toIndex: Int) {
|
||||
for (index in fromIndex until toIndex) resetAt(index)
|
||||
}
|
||||
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.builders
|
||||
|
||||
@PublishedApi
|
||||
internal class MapBuilder<K, V> private constructor(
|
||||
private var keysArray: Array<K>,
|
||||
private var valuesArray: Array<V>?, // allocated only when actually used, always null in pure HashSet
|
||||
private var presenceArray: IntArray,
|
||||
private var hashArray: IntArray,
|
||||
private var maxProbeDistance: Int,
|
||||
private var length: Int
|
||||
) : MutableMap<K, V> {
|
||||
private var hashShift: Int = computeShift(hashSize)
|
||||
|
||||
override var size: Int = 0
|
||||
private set
|
||||
|
||||
private var keysView: HashMapKeys<K>? = null
|
||||
private var valuesView: HashMapValues<V>? = null
|
||||
private var entriesView: HashMapEntrySet<K, V>? = null
|
||||
|
||||
private var isReadOnly: Boolean = false
|
||||
|
||||
// ---------------------------- functions ----------------------------
|
||||
|
||||
constructor() : this(INITIAL_CAPACITY)
|
||||
|
||||
constructor(initialCapacity: Int) : this(
|
||||
arrayOfUninitializedElements(initialCapacity),
|
||||
null,
|
||||
IntArray(initialCapacity),
|
||||
IntArray(computeHashSize(initialCapacity)),
|
||||
INITIAL_MAX_PROBE_DISTANCE,
|
||||
0)
|
||||
|
||||
fun build(): Map<K, V> {
|
||||
checkIsMutable()
|
||||
isReadOnly = true
|
||||
return this
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean = size == 0
|
||||
override fun containsKey(key: K): Boolean = findKey(key) >= 0
|
||||
override fun containsValue(value: V): Boolean = findValue(value) >= 0
|
||||
|
||||
override operator fun get(key: K): V? {
|
||||
val index = findKey(key)
|
||||
if (index < 0) return null
|
||||
return valuesArray!![index]
|
||||
}
|
||||
|
||||
override fun put(key: K, value: V): V? {
|
||||
checkIsMutable()
|
||||
val index = addKey(key)
|
||||
val valuesArray = allocateValuesArray()
|
||||
if (index < 0) {
|
||||
val oldValue = valuesArray[-index - 1]
|
||||
valuesArray[-index - 1] = value
|
||||
return oldValue
|
||||
} else {
|
||||
valuesArray[index] = value
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun putAll(from: Map<out K, V>) {
|
||||
putAllEntries(from.entries) // mutability gets checked here
|
||||
}
|
||||
|
||||
override fun remove(key: K): V? {
|
||||
val index = removeKey(key) // mutability gets checked here
|
||||
if (index < 0) return null
|
||||
val valuesArray = valuesArray!!
|
||||
val oldValue = valuesArray[index]
|
||||
valuesArray.resetAt(index)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
checkIsMutable()
|
||||
// O(length) implementation for hashArray cleanup
|
||||
for (i in 0..length - 1) {
|
||||
val hash = presenceArray[i]
|
||||
if (hash >= 0) {
|
||||
hashArray[hash] = 0
|
||||
presenceArray[i] = TOMBSTONE
|
||||
}
|
||||
}
|
||||
keysArray.resetRange(0, length)
|
||||
valuesArray?.resetRange(0, length)
|
||||
size = 0
|
||||
length = 0
|
||||
}
|
||||
|
||||
override val keys: MutableSet<K> get() {
|
||||
val cur = keysView
|
||||
return if (cur == null) {
|
||||
val new = HashMapKeys(this)
|
||||
keysView = new
|
||||
new
|
||||
} else cur
|
||||
}
|
||||
|
||||
override val values: MutableCollection<V> get() {
|
||||
val cur = valuesView
|
||||
return if (cur == null) {
|
||||
val new = HashMapValues(this)
|
||||
valuesView = new
|
||||
new
|
||||
} else cur
|
||||
}
|
||||
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<K, V>> get() {
|
||||
val cur = entriesView
|
||||
return if (cur == null) {
|
||||
val new = HashMapEntrySet(this)
|
||||
entriesView = new
|
||||
return new
|
||||
} else cur
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other === this ||
|
||||
(other is Map<*, *>) &&
|
||||
contentEquals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = 0
|
||||
val it = entriesIterator()
|
||||
while (it.hasNext()) {
|
||||
result += it.nextHashCode()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val sb = StringBuilder(2 + size * 3)
|
||||
sb.append("{")
|
||||
var i = 0
|
||||
val it = entriesIterator()
|
||||
while (it.hasNext()) {
|
||||
if (i > 0) sb.append(", ")
|
||||
it.nextAppendString(sb)
|
||||
i++
|
||||
}
|
||||
sb.append("}")
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
// ---------------------------- private ----------------------------
|
||||
|
||||
private val capacity: Int get() = keysArray.size
|
||||
private val hashSize: Int get() = hashArray.size
|
||||
|
||||
private fun checkIsMutable() {
|
||||
if (isReadOnly) throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
private fun ensureExtraCapacity(n: Int) {
|
||||
ensureCapacity(length + n)
|
||||
}
|
||||
|
||||
private fun ensureCapacity(capacity: Int) {
|
||||
if (capacity > this.capacity) {
|
||||
var newSize = this.capacity * 3 / 2
|
||||
if (capacity > newSize) newSize = capacity
|
||||
keysArray = keysArray.copyOfUninitializedElements(newSize)
|
||||
valuesArray = valuesArray?.copyOfUninitializedElements(newSize)
|
||||
presenceArray = presenceArray.copyOf(newSize)
|
||||
val newHashSize = computeHashSize(newSize)
|
||||
if (newHashSize > hashSize) rehash(newHashSize)
|
||||
} else if (length + capacity - size > this.capacity) {
|
||||
rehash(hashSize)
|
||||
}
|
||||
}
|
||||
|
||||
private fun allocateValuesArray(): Array<V> {
|
||||
val curValuesArray = valuesArray
|
||||
if (curValuesArray != null) return curValuesArray
|
||||
val newValuesArray = arrayOfUninitializedElements<V>(capacity)
|
||||
valuesArray = newValuesArray
|
||||
return newValuesArray
|
||||
}
|
||||
|
||||
private fun hash(key: K) = (key.hashCode() * MAGIC) ushr hashShift
|
||||
|
||||
private fun compact() {
|
||||
var i = 0
|
||||
var j = 0
|
||||
val valuesArray = valuesArray
|
||||
while (i < length) {
|
||||
if (presenceArray[i] >= 0) {
|
||||
keysArray[j] = keysArray[i]
|
||||
if (valuesArray != null) valuesArray[j] = valuesArray[i]
|
||||
j++
|
||||
}
|
||||
i++
|
||||
}
|
||||
keysArray.resetRange(j, length)
|
||||
valuesArray?.resetRange(j, length)
|
||||
length = j
|
||||
//check(length == size) { "Internal invariant violated during compact: length=$length != size=$size" }
|
||||
}
|
||||
|
||||
private fun rehash(newHashSize: Int) {
|
||||
if (length > size) compact()
|
||||
if (newHashSize != hashSize) {
|
||||
hashArray = IntArray(newHashSize)
|
||||
hashShift = computeShift(newHashSize)
|
||||
} else {
|
||||
hashArray.fill(0, 0, hashSize)
|
||||
}
|
||||
var i = 0
|
||||
while (i < length) {
|
||||
if (!putRehash(i++)) {
|
||||
throw IllegalStateException("This cannot happen with fixed magic multiplier and grow-only hash array. " +
|
||||
"Have object hashCodes changed?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun putRehash(i: Int): Boolean {
|
||||
var hash = hash(keysArray[i])
|
||||
var probesLeft = maxProbeDistance
|
||||
while (true) {
|
||||
val index = hashArray[hash]
|
||||
if (index == 0) {
|
||||
hashArray[hash] = i + 1
|
||||
presenceArray[i] = hash
|
||||
return true
|
||||
}
|
||||
if (--probesLeft < 0) return false
|
||||
if (hash-- == 0) hash = hashSize - 1
|
||||
}
|
||||
}
|
||||
|
||||
private fun findKey(key: K): Int {
|
||||
var hash = hash(key)
|
||||
var probesLeft = maxProbeDistance
|
||||
while (true) {
|
||||
val index = hashArray[hash]
|
||||
if (index == 0) return TOMBSTONE
|
||||
if (index > 0 && keysArray[index - 1] == key) return index - 1
|
||||
if (--probesLeft < 0) return TOMBSTONE
|
||||
if (hash-- == 0) hash = hashSize - 1
|
||||
}
|
||||
}
|
||||
|
||||
private fun findValue(value: V): Int {
|
||||
var i = length
|
||||
while (--i >= 0) {
|
||||
if (presenceArray[i] >= 0 && valuesArray!![i] == value)
|
||||
return i
|
||||
}
|
||||
return TOMBSTONE
|
||||
}
|
||||
|
||||
internal fun addKey(key: K): Int {
|
||||
checkIsMutable()
|
||||
retry@ while (true) {
|
||||
var hash = hash(key)
|
||||
// put is allowed to grow maxProbeDistance with some limits (resize hash on reaching limits)
|
||||
val tentativeMaxProbeDistance = (maxProbeDistance * 2).coerceAtMost(hashSize / 2)
|
||||
var probeDistance = 0
|
||||
while (true) {
|
||||
val index = hashArray[hash]
|
||||
if (index <= 0) { // claim or reuse hash slot
|
||||
if (length >= capacity) {
|
||||
ensureExtraCapacity(1)
|
||||
continue@retry
|
||||
}
|
||||
val putIndex = length++
|
||||
keysArray[putIndex] = key
|
||||
presenceArray[putIndex] = hash
|
||||
hashArray[hash] = putIndex + 1
|
||||
size++
|
||||
if (probeDistance > maxProbeDistance) maxProbeDistance = probeDistance
|
||||
return putIndex
|
||||
}
|
||||
if (keysArray[index - 1] == key) {
|
||||
return -index
|
||||
}
|
||||
if (++probeDistance > tentativeMaxProbeDistance) {
|
||||
rehash(hashSize * 2) // cannot find room even with extra "tentativeMaxProbeDistance" -- grow hash
|
||||
continue@retry
|
||||
}
|
||||
if (hash-- == 0) hash = hashSize - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun removeKey(key: K): Int {
|
||||
checkIsMutable()
|
||||
val index = findKey(key)
|
||||
if (index < 0) return TOMBSTONE
|
||||
removeKeyAt(index)
|
||||
return index
|
||||
}
|
||||
|
||||
private fun removeKeyAt(index: Int) {
|
||||
keysArray.resetAt(index)
|
||||
removeHashAt(presenceArray[index])
|
||||
presenceArray[index] = TOMBSTONE
|
||||
size--
|
||||
}
|
||||
|
||||
private fun removeHashAt(removedHash: Int) {
|
||||
var hash = removedHash
|
||||
var hole = removedHash // will try to patch the hole in hash array
|
||||
var probeDistance = 0
|
||||
var patchAttemptsLeft = (maxProbeDistance * 2).coerceAtMost(hashSize / 2) // don't spend too much effort
|
||||
while (true) {
|
||||
if (hash-- == 0) hash = hashSize - 1
|
||||
if (++probeDistance > maxProbeDistance) {
|
||||
// too far away -- can release the hole, bad case will not happen
|
||||
hashArray[hole] = 0
|
||||
return
|
||||
}
|
||||
val index = hashArray[hash]
|
||||
if (index == 0) {
|
||||
// end of chain -- can release the hole, bad case will not happen
|
||||
hashArray[hole] = 0
|
||||
return
|
||||
}
|
||||
if (index < 0) {
|
||||
// TOMBSTONE FOUND
|
||||
// - <--- [ TS ] ------ [hole] ---> +
|
||||
// \------------/
|
||||
// probeDistance
|
||||
// move tombstone into the hole
|
||||
hashArray[hole] = TOMBSTONE
|
||||
hole = hash
|
||||
probeDistance = 0
|
||||
} else {
|
||||
val otherHash = hash(keysArray[index - 1])
|
||||
// Bad case:
|
||||
// - <--- [hash] ------ [hole] ------ [otherHash] ---> +
|
||||
// \------------/
|
||||
// probeDistance
|
||||
if ((otherHash - hash) and (hashSize - 1) >= probeDistance) {
|
||||
// move otherHash into the hole, move the hole
|
||||
hashArray[hole] = index
|
||||
presenceArray[index - 1] = hole
|
||||
hole = hash
|
||||
probeDistance = 0
|
||||
}
|
||||
}
|
||||
// check how long we're patching holes
|
||||
if (--patchAttemptsLeft < 0) {
|
||||
// just place tombstone into the hole
|
||||
hashArray[hole] = TOMBSTONE
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun containsEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
val index = findKey(entry.key)
|
||||
if (index < 0) return false
|
||||
return valuesArray!![index] == entry.value
|
||||
}
|
||||
|
||||
private fun contentEquals(other: Map<*, *>): Boolean = size == other.size && containsAllEntries(other.entries)
|
||||
|
||||
internal fun containsAllEntries(m: Collection<*>): Boolean {
|
||||
val it = m.iterator()
|
||||
while (it.hasNext()) {
|
||||
val entry = it.next()
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST") // todo: get rid of unchecked cast here somehow
|
||||
if (entry == null || !containsEntry(entry as Map.Entry<K, V>))
|
||||
return false
|
||||
} catch (e: ClassCastException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun putEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
checkIsMutable()
|
||||
val index = addKey(entry.key)
|
||||
val valuesArray = allocateValuesArray()
|
||||
if (index >= 0) {
|
||||
valuesArray[index] = entry.value
|
||||
return true
|
||||
}
|
||||
val oldValue = valuesArray[-index - 1]
|
||||
if (entry.value != oldValue) {
|
||||
valuesArray[-index - 1] = entry.value
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
internal fun putAllEntries(from: Collection<Map.Entry<K, V>>): Boolean {
|
||||
checkIsMutable()
|
||||
if (from.isEmpty()) return false
|
||||
ensureExtraCapacity(from.size)
|
||||
val it = from.iterator()
|
||||
var updated = false
|
||||
while (it.hasNext()) {
|
||||
if (putEntry(it.next()))
|
||||
updated = true
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
internal fun removeEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
checkIsMutable()
|
||||
val index = findKey(entry.key)
|
||||
if (index < 0) return false
|
||||
if (valuesArray!![index] != entry.value) return false
|
||||
removeKeyAt(index)
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun removeValue(element: V): Boolean {
|
||||
checkIsMutable()
|
||||
val index = findValue(element)
|
||||
if (index < 0) return false
|
||||
removeKeyAt(index)
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun keysIterator() = KeysItr(this)
|
||||
internal fun valuesIterator() = ValuesItr(this)
|
||||
internal fun entriesIterator() = EntriesItr(this)
|
||||
|
||||
private companion object {
|
||||
private const val MAGIC = -1640531527 // 2654435769L.toInt(), golden ratio
|
||||
private const val INITIAL_CAPACITY = 8
|
||||
private const val INITIAL_MAX_PROBE_DISTANCE = 2
|
||||
private const val TOMBSTONE = -1
|
||||
|
||||
@UseExperimental(ExperimentalStdlibApi::class)
|
||||
private fun computeHashSize(capacity: Int): Int = (capacity.coerceAtLeast(1) * 3).takeHighestOneBit()
|
||||
|
||||
@UseExperimental(ExperimentalStdlibApi::class)
|
||||
private fun computeShift(hashSize: Int): Int = hashSize.countLeadingZeroBits() + 1
|
||||
}
|
||||
|
||||
internal open class Itr<K, V>(
|
||||
internal val map: MapBuilder<K, V>
|
||||
) {
|
||||
internal var index = 0
|
||||
internal var lastIndex: Int = -1
|
||||
|
||||
init {
|
||||
initNext()
|
||||
}
|
||||
|
||||
internal fun initNext() {
|
||||
while (index < map.length && map.presenceArray[index] < 0)
|
||||
index++
|
||||
}
|
||||
|
||||
fun hasNext(): Boolean = index < map.length
|
||||
|
||||
fun remove() {
|
||||
map.checkIsMutable()
|
||||
map.removeKeyAt(lastIndex)
|
||||
lastIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
internal class KeysItr<K, V>(map: MapBuilder<K, V>) : Itr<K, V>(map), MutableIterator<K> {
|
||||
override fun next(): K {
|
||||
if (index >= map.length) throw NoSuchElementException()
|
||||
lastIndex = index++
|
||||
val result = map.keysArray[lastIndex]
|
||||
initNext()
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class ValuesItr<K, V>(map: MapBuilder<K, V>) : Itr<K, V>(map), MutableIterator<V> {
|
||||
override fun next(): V {
|
||||
if (index >= map.length) throw NoSuchElementException()
|
||||
lastIndex = index++
|
||||
val result = map.valuesArray!![lastIndex]
|
||||
initNext()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
internal class EntriesItr<K, V>(map: MapBuilder<K, V>) : Itr<K, V>(map),
|
||||
MutableIterator<MutableMap.MutableEntry<K, V>> {
|
||||
override fun next(): EntryRef<K, V> {
|
||||
if (index >= map.length) throw NoSuchElementException()
|
||||
lastIndex = index++
|
||||
val result = EntryRef(map, lastIndex)
|
||||
initNext()
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun nextHashCode(): Int {
|
||||
if (index >= map.length) throw NoSuchElementException()
|
||||
lastIndex = index++
|
||||
val result = map.keysArray[lastIndex].hashCode() xor map.valuesArray!![lastIndex].hashCode()
|
||||
initNext()
|
||||
return result
|
||||
}
|
||||
|
||||
fun nextAppendString(sb: StringBuilder) {
|
||||
if (index >= map.length) throw NoSuchElementException()
|
||||
lastIndex = index++
|
||||
val key = map.keysArray[lastIndex]
|
||||
if (key == map) sb.append("(this Map)") else sb.append(key)
|
||||
sb.append('=')
|
||||
val value = map.valuesArray!![lastIndex]
|
||||
if (value == map) sb.append("(this Map)") else sb.append(value)
|
||||
initNext()
|
||||
}
|
||||
}
|
||||
|
||||
internal class EntryRef<K, V>(
|
||||
private val map: MapBuilder<K, V>,
|
||||
private val index: Int
|
||||
) : MutableMap.MutableEntry<K, V> {
|
||||
override val key: K
|
||||
get() = map.keysArray[index]
|
||||
|
||||
override val value: V
|
||||
get() = map.valuesArray!![index]
|
||||
|
||||
override fun setValue(newValue: V): V {
|
||||
map.checkIsMutable()
|
||||
val valuesArray = map.allocateValuesArray()
|
||||
val oldValue = valuesArray[index]
|
||||
valuesArray[index] = newValue
|
||||
return oldValue
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is Map.Entry<*, *> &&
|
||||
other.key == key &&
|
||||
other.value == value
|
||||
|
||||
override fun hashCode(): Int = key.hashCode() xor value.hashCode()
|
||||
|
||||
override fun toString(): String = "$key=$value"
|
||||
}
|
||||
}
|
||||
|
||||
internal class HashMapKeys<E> internal constructor(
|
||||
private val backing: MapBuilder<E, *>
|
||||
) : MutableSet<E>, AbstractMutableSet<E>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||
override fun contains(element: E): Boolean = backing.containsKey(element)
|
||||
override fun clear() = backing.clear()
|
||||
override fun add(element: E): Boolean = throw UnsupportedOperationException()
|
||||
override fun addAll(elements: Collection<E>): Boolean = throw UnsupportedOperationException()
|
||||
override fun remove(element: E): Boolean = backing.removeKey(element) >= 0
|
||||
override fun iterator(): MutableIterator<E> = backing.keysIterator()
|
||||
}
|
||||
|
||||
internal class HashMapValues<V> internal constructor(
|
||||
val backing: MapBuilder<*, V>
|
||||
) : MutableCollection<V>, AbstractMutableCollection<V>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||
override fun contains(element: V): Boolean = backing.containsValue(element)
|
||||
override fun add(element: V): Boolean = throw UnsupportedOperationException()
|
||||
override fun addAll(elements: Collection<V>): Boolean = throw UnsupportedOperationException()
|
||||
override fun clear() = backing.clear()
|
||||
override fun iterator(): MutableIterator<V> = backing.valuesIterator()
|
||||
override fun remove(element: V): Boolean = backing.removeValue(element)
|
||||
}
|
||||
|
||||
internal class HashMapEntrySet<K, V> internal constructor(
|
||||
val backing: MapBuilder<K, V>
|
||||
) : MutableSet<MutableMap.MutableEntry<K, V>>, AbstractMutableSet<MutableMap.MutableEntry<K, V>>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||
override fun contains(element: MutableMap.MutableEntry<K, V>): Boolean = backing.containsEntry(element)
|
||||
override fun clear() = backing.clear()
|
||||
override fun add(element: MutableMap.MutableEntry<K, V>): Boolean = backing.putEntry(element)
|
||||
override fun remove(element: MutableMap.MutableEntry<K, V>): Boolean = backing.removeEntry(element)
|
||||
override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> = backing.entriesIterator()
|
||||
override fun containsAll(elements: Collection<MutableMap.MutableEntry<K, V>>): Boolean = backing.containsAllEntries(elements)
|
||||
override fun addAll(elements: Collection<MutableMap.MutableEntry<K, V>>): Boolean = backing.putAllEntries(elements)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.builders
|
||||
|
||||
@PublishedApi
|
||||
internal class SetBuilder<E> internal constructor(
|
||||
private val backing: MapBuilder<E, *>
|
||||
) : MutableSet<E>, AbstractMutableSet<E>() {
|
||||
|
||||
constructor() : this(MapBuilder<E, Nothing>())
|
||||
|
||||
constructor(initialCapacity: Int) : this(MapBuilder<E, Nothing>(initialCapacity))
|
||||
|
||||
fun build(): Set<E> {
|
||||
backing.build()
|
||||
return this
|
||||
}
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||
override fun contains(element: E): Boolean = backing.containsKey(element)
|
||||
override fun clear() = backing.clear()
|
||||
override fun add(element: E): Boolean = backing.addKey(element) >= 0
|
||||
override fun remove(element: E): Boolean = backing.removeKey(element) >= 0
|
||||
override fun iterator(): MutableIterator<E> = backing.keysIterator()
|
||||
}
|
||||
Reference in New Issue
Block a user