[JS IR] Rework HashMapEntries, get rid of LinkedHashMap
^KT-59001
This commit is contained in:
committed by
Space Team
parent
f202865080
commit
2300facead
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2023 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.
|
||||
*/
|
||||
|
||||
@@ -20,82 +20,22 @@ package kotlin.collections
|
||||
*/
|
||||
public actual abstract class AbstractMutableMap<K, V> protected actual constructor() : AbstractMap<K, V>(), MutableMap<K, V> {
|
||||
|
||||
/**
|
||||
* A mutable [Map.Entry] shared by several [Map] implementations.
|
||||
*/
|
||||
internal open class SimpleEntry<K, V>(override val key: K, value: V) : MutableMap.MutableEntry<K, V> {
|
||||
constructor(entry: Map.Entry<K, V>) : this(entry.key, entry.value)
|
||||
internal open fun createKeysView(): MutableSet<K> = HashMapKeysDefault(this)
|
||||
internal open fun createValuesView(): MutableCollection<V> = HashMapValuesDefault(this)
|
||||
|
||||
private var _value = value
|
||||
private var keysView: MutableSet<K>? = null
|
||||
private var valuesView: MutableCollection<V>? = null
|
||||
|
||||
override val value: V get() = _value
|
||||
actual override val keys: MutableSet<K>
|
||||
get() = keysView ?: createKeysView().also { keysView = it }
|
||||
|
||||
override fun setValue(newValue: V): V {
|
||||
// Should check if the map containing this entry is mutable.
|
||||
// However, to not increase entry memory footprint it might be worthwhile not to check it here and
|
||||
// force subclasses that implement `build()` (freezing) operation to implement their own `MutableEntry`.
|
||||
// this@AbstractMutableMap.checkIsMutable()
|
||||
val oldValue = this._value
|
||||
this._value = newValue
|
||||
return oldValue
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = entryHashCode(this)
|
||||
override fun toString(): String = entryToString(this)
|
||||
override fun equals(other: Any?): Boolean = entryEquals(this, other)
|
||||
|
||||
}
|
||||
|
||||
// intermediate abstract class to workaround KT-43321
|
||||
internal abstract class AbstractEntrySet<E : Map.Entry<K, V>, K, V> : AbstractMutableSet<E>() {
|
||||
final override fun contains(element: E): Boolean = containsEntry(element)
|
||||
abstract fun containsEntry(element: Map.Entry<K, V>): Boolean
|
||||
final override fun remove(element: E): Boolean = removeEntry(element)
|
||||
abstract fun removeEntry(element: Map.Entry<K, V>): Boolean
|
||||
}
|
||||
actual override val values: MutableCollection<V>
|
||||
get() = valuesView ?: createValuesView().also { valuesView = it }
|
||||
|
||||
actual override fun clear() {
|
||||
entries.clear()
|
||||
}
|
||||
|
||||
private var _keys: MutableSet<K>? = null
|
||||
actual override val keys: MutableSet<K>
|
||||
get() {
|
||||
if (_keys == null) {
|
||||
_keys = object : AbstractMutableSet<K>() {
|
||||
override fun add(element: K): Boolean = throw UnsupportedOperationException("Add is not supported on keys")
|
||||
override fun clear() {
|
||||
this@AbstractMutableMap.clear()
|
||||
}
|
||||
|
||||
override operator fun contains(element: K): Boolean = containsKey(element)
|
||||
|
||||
override operator fun iterator(): MutableIterator<K> {
|
||||
val entryIterator = entries.iterator()
|
||||
return object : MutableIterator<K> {
|
||||
override fun hasNext(): Boolean = entryIterator.hasNext()
|
||||
override fun next(): K = entryIterator.next().key
|
||||
override fun remove() = entryIterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(element: K): Boolean {
|
||||
checkIsMutable()
|
||||
if (containsKey(element)) {
|
||||
this@AbstractMutableMap.remove(element)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override val size: Int get() = this@AbstractMutableMap.size
|
||||
|
||||
override fun checkIsMutable(): Unit = this@AbstractMutableMap.checkIsMutable()
|
||||
}
|
||||
}
|
||||
return _keys!!
|
||||
}
|
||||
|
||||
actual abstract override fun put(key: K, value: V): V?
|
||||
|
||||
actual override fun putAll(from: Map<out K, V>) {
|
||||
@@ -105,33 +45,6 @@ public actual abstract class AbstractMutableMap<K, V> protected actual construct
|
||||
}
|
||||
}
|
||||
|
||||
private var _values: MutableCollection<V>? = null
|
||||
actual override val values: MutableCollection<V>
|
||||
get() {
|
||||
if (_values == null) {
|
||||
_values = object : AbstractMutableCollection<V>() {
|
||||
override fun add(element: V): Boolean = throw UnsupportedOperationException("Add is not supported on values")
|
||||
override fun clear() = this@AbstractMutableMap.clear()
|
||||
|
||||
override operator fun contains(element: V): Boolean = containsValue(element)
|
||||
|
||||
override operator fun iterator(): MutableIterator<V> {
|
||||
val entryIterator = entries.iterator()
|
||||
return object : MutableIterator<V> {
|
||||
override fun hasNext(): Boolean = entryIterator.hasNext()
|
||||
override fun next(): V = entryIterator.next().value
|
||||
override fun remove() = entryIterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
override val size: Int get() = this@AbstractMutableMap.size
|
||||
|
||||
override fun checkIsMutable(): Unit = this@AbstractMutableMap.checkIsMutable()
|
||||
}
|
||||
}
|
||||
return _values!!
|
||||
}
|
||||
|
||||
actual override fun remove(key: K): V? {
|
||||
checkIsMutable()
|
||||
val iter = entries.iterator()
|
||||
@@ -152,5 +65,5 @@ public actual abstract class AbstractMutableMap<K, V> protected actual construct
|
||||
* This method is called every time when a mutating method is called on this mutable map.
|
||||
* Mutable maps that are built (frozen) must throw `UnsupportedOperationException`.
|
||||
*/
|
||||
internal open fun checkIsMutable(): Unit {}
|
||||
internal open fun checkIsMutable() {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2023 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.
|
||||
*/
|
||||
|
||||
@@ -20,34 +20,10 @@ import kotlin.collections.MutableMap.MutableEntry
|
||||
// Classes that extend HashMap and implement `build()` (freezing) operation
|
||||
// have to make sure mutating methods check `checkIsMutable`.
|
||||
public actual open class HashMap<K, V> : AbstractMutableMap<K, V>, MutableMap<K, V> {
|
||||
|
||||
private inner class EntrySet : AbstractEntrySet<MutableEntry<K, V>, K, V>() {
|
||||
|
||||
override fun add(element: MutableEntry<K, V>): Boolean = throw UnsupportedOperationException("Add is not supported on entries")
|
||||
override fun clear() {
|
||||
this@HashMap.clear()
|
||||
}
|
||||
|
||||
override fun containsEntry(element: Map.Entry<K, V>): Boolean = this@HashMap.containsEntry(element)
|
||||
|
||||
override operator fun iterator(): MutableIterator<MutableEntry<K, V>> = internalMap.entriesIterator()
|
||||
|
||||
override fun removeEntry(element: Map.Entry<K, V>): Boolean {
|
||||
if (contains(element)) {
|
||||
this@HashMap.remove(element.key)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override val size: Int get() = this@HashMap.size
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal implementation of the map: either string-based or hashcode-based.
|
||||
*/
|
||||
private val internalMap: InternalMap<K, V>
|
||||
internal val internalMap: InternalMap<K, V>
|
||||
|
||||
internal constructor(internalMap: InternalMap<K, V>) : super() {
|
||||
this.internalMap = internalMap
|
||||
@@ -92,7 +68,6 @@ public actual open class HashMap<K, V> : AbstractMutableMap<K, V>, MutableMap<K,
|
||||
*/
|
||||
actual constructor(initialCapacity: Int) : this(initialCapacity, 1.0f)
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new [HashMap] filled with the contents of the specified [original] map.
|
||||
*/
|
||||
@@ -109,16 +84,12 @@ public actual open class HashMap<K, V> : AbstractMutableMap<K, V>, MutableMap<K,
|
||||
|
||||
actual override fun containsValue(value: V): Boolean = internalMap.containsValue(value)
|
||||
|
||||
private var _entries: MutableSet<MutableMap.MutableEntry<K, V>>? = null
|
||||
actual override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
|
||||
get() {
|
||||
if (_entries == null) {
|
||||
_entries = createEntrySet()
|
||||
}
|
||||
return _entries!!
|
||||
}
|
||||
override fun createKeysView(): MutableSet<K> = HashMapKeys(internalMap)
|
||||
override fun createValuesView(): MutableCollection<V> = HashMapValues(internalMap)
|
||||
|
||||
internal open fun createEntrySet(): MutableSet<MutableMap.MutableEntry<K, V>> = EntrySet()
|
||||
private var entriesView: HashMapEntrySet<K, V>? = null
|
||||
actual override val entries: MutableSet<MutableEntry<K, V>>
|
||||
get() = entriesView ?: HashMapEntrySet(internalMap).also { entriesView = it }
|
||||
|
||||
actual override operator fun get(key: K): V? = internalMap.get(key)
|
||||
|
||||
@@ -128,6 +99,7 @@ public actual open class HashMap<K, V> : AbstractMutableMap<K, V>, MutableMap<K,
|
||||
|
||||
actual override val size: Int get() = internalMap.size
|
||||
|
||||
actual override fun putAll(from: Map<out K, V>) = internalMap.putAll(from)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
internal class HashMapKeys<E> internal constructor(
|
||||
private val backing: InternalMap<E, *>,
|
||||
) : MutableSet<E>, AbstractMutableSet<E>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
override fun contains(element: E): Boolean = backing.contains(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.remove(element) != null
|
||||
override fun iterator(): MutableIterator<E> = backing.keysIterator()
|
||||
|
||||
override fun checkIsMutable() = backing.checkIsMutable()
|
||||
}
|
||||
|
||||
internal class HashMapValues<V> internal constructor(
|
||||
private val backing: InternalMap<*, V>,
|
||||
) : MutableCollection<V>, AbstractMutableCollection<V>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
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)
|
||||
|
||||
override fun checkIsMutable() = backing.checkIsMutable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: intermediate class with [E] `: Map.Entry<K, V>` is required to support
|
||||
* [contains] for values that are [Map.Entry] but not [MutableMap.MutableEntry],
|
||||
* and probably same for other functions.
|
||||
* This is important because an instance of this class can be used as a result of [Map.entries],
|
||||
* which should support [contains] for [Map.Entry].
|
||||
* For example, this happens when upcasting [MutableMap] to [Map].
|
||||
*
|
||||
* The compiler enables special type-safe barriers to methods like [contains], which has [UnsafeVariance].
|
||||
* Changing type from [MutableMap.MutableEntry] to [E] makes the compiler generate barriers checking that
|
||||
* argument `is` [E] (so technically `is` [Map.Entry]) instead of `is` [MutableMap.MutableEntry].
|
||||
*
|
||||
* See also [KT-42248](https://youtrack.jetbrains.com/issue/KT-42428).
|
||||
*/
|
||||
internal abstract class HashMapEntrySetBase<K, V, E : Map.Entry<K, V>> internal constructor(
|
||||
val backing: InternalMap<K, V>,
|
||||
) : MutableSet<E>, AbstractMutableSet<E>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
override fun contains(element: E): Boolean = backing.containsEntry(element)
|
||||
protected abstract fun getEntry(element: Map.Entry<K, V>): E?
|
||||
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.removeEntry(element)
|
||||
override fun containsAll(elements: Collection<E>): Boolean = backing.containsAllEntries(elements)
|
||||
|
||||
override fun checkIsMutable() = backing.checkIsMutable()
|
||||
}
|
||||
|
||||
internal class HashMapEntrySet<K, V> internal constructor(
|
||||
backing: InternalMap<K, V>,
|
||||
) : HashMapEntrySetBase<K, V, MutableMap.MutableEntry<K, V>>(backing) {
|
||||
|
||||
override fun getEntry(element: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>? = backing.getEntry(element)
|
||||
|
||||
override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> = backing.entriesIterator()
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
internal class HashMapKeysDefault<K, V>(private val backingMap: AbstractMutableMap<K, V>) : AbstractMutableSet<K>() {
|
||||
override fun add(element: K): Boolean = throw UnsupportedOperationException("Add is not supported on keys")
|
||||
override fun clear() = backingMap.clear()
|
||||
override operator fun contains(element: K): Boolean = backingMap.containsKey(element)
|
||||
|
||||
override operator fun iterator(): MutableIterator<K> {
|
||||
val entryIterator = backingMap.entries.iterator()
|
||||
return object : MutableIterator<K> {
|
||||
override fun hasNext(): Boolean = entryIterator.hasNext()
|
||||
override fun next(): K = entryIterator.next().key
|
||||
override fun remove() = entryIterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(element: K): Boolean {
|
||||
checkIsMutable()
|
||||
if (backingMap.containsKey(element)) {
|
||||
backingMap.remove(element)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override val size: Int get() = backingMap.size
|
||||
|
||||
override fun checkIsMutable(): Unit = backingMap.checkIsMutable()
|
||||
}
|
||||
|
||||
internal class HashMapValuesDefault<K, V>(private val backingMap: AbstractMutableMap<K, V>) : AbstractMutableCollection<V>() {
|
||||
override fun add(element: V): Boolean = throw UnsupportedOperationException("Add is not supported on values")
|
||||
override fun clear() = backingMap.clear()
|
||||
|
||||
override operator fun contains(element: V): Boolean = backingMap.containsValue(element)
|
||||
|
||||
override operator fun iterator(): MutableIterator<V> {
|
||||
val entryIterator = backingMap.entries.iterator()
|
||||
return object : MutableIterator<V> {
|
||||
override fun hasNext(): Boolean = entryIterator.hasNext()
|
||||
override fun next(): V = entryIterator.next().value
|
||||
override fun remove() = entryIterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
override val size: Int get() = backingMap.size
|
||||
|
||||
override fun checkIsMutable(): Unit = backingMap.checkIsMutable()
|
||||
}
|
||||
@@ -19,10 +19,6 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
override val size: Int
|
||||
get() = _size
|
||||
|
||||
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 ----------------------------
|
||||
@@ -78,11 +74,9 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
require(loadFactor > 0) { "Non-positive load factor: $loadFactor" }
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun build(): InternalHashMap<K, V> {
|
||||
override fun build() {
|
||||
checkIsMutable()
|
||||
isReadOnly = true
|
||||
return if (size > 0) this else EmptyHolder.value()
|
||||
}
|
||||
|
||||
fun isEmpty(): Boolean = _size == 0
|
||||
@@ -143,36 +137,6 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
length = 0
|
||||
}
|
||||
|
||||
val keys: MutableSet<K>
|
||||
get() {
|
||||
val cur = keysView
|
||||
return if (cur == null) {
|
||||
val new = HashMapKeys(this)
|
||||
keysView = new
|
||||
new
|
||||
} else cur
|
||||
}
|
||||
|
||||
val values: MutableCollection<V>
|
||||
get() {
|
||||
val cur = valuesView
|
||||
return if (cur == null) {
|
||||
val new = HashMapValues(this)
|
||||
valuesView = new
|
||||
new
|
||||
} else cur
|
||||
}
|
||||
|
||||
val entries: MutableSet<MutableMap.MutableEntry<K, V>>
|
||||
get() {
|
||||
val cur = entriesView
|
||||
return if (cur == null) {
|
||||
val new = HashMapEntrySet(this)
|
||||
entriesView = new
|
||||
new
|
||||
} else cur
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other === this ||
|
||||
(other is Map<*, *>) &&
|
||||
@@ -488,9 +452,9 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun removeValue(element: V): Boolean {
|
||||
override fun removeValue(value: V): Boolean {
|
||||
checkIsMutable()
|
||||
val index = findValue(element)
|
||||
val index = findValue(value)
|
||||
if (index < 0) return false
|
||||
removeKeyAt(index)
|
||||
return true
|
||||
@@ -511,15 +475,6 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
private fun computeShift(hashSize: Int): Int = hashSize.countLeadingZeroBits() + 1
|
||||
}
|
||||
|
||||
internal object EmptyHolder {
|
||||
val value_ = InternalHashMap<Nothing, Nothing>(0).also { it.isReadOnly = true }
|
||||
|
||||
fun <K, V> value(): InternalHashMap<K, V> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return value_ as InternalHashMap<K, V>
|
||||
}
|
||||
}
|
||||
|
||||
internal open class Itr<K, V>(
|
||||
internal val map: InternalHashMap<K, V>,
|
||||
) {
|
||||
@@ -622,99 +577,3 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
override fun toString(): String = "$key=$value"
|
||||
}
|
||||
}
|
||||
|
||||
internal class HashMapKeys<E> internal constructor(
|
||||
private val backing: InternalMap<E, *>,
|
||||
) : MutableSet<E>, AbstractMutableSet<E>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
override fun contains(element: E): Boolean = backing.contains(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.remove(element) != null
|
||||
override fun iterator(): MutableIterator<E> = backing.keysIterator()
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean {
|
||||
backing.checkIsMutable()
|
||||
return super.removeAll(elements)
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<E>): Boolean {
|
||||
backing.checkIsMutable()
|
||||
return super.retainAll(elements)
|
||||
}
|
||||
}
|
||||
|
||||
internal class HashMapValues<V> internal constructor(
|
||||
private val backing: InternalMap<*, V>,
|
||||
) : MutableCollection<V>, AbstractMutableCollection<V>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
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)
|
||||
|
||||
override fun removeAll(elements: Collection<V>): Boolean {
|
||||
backing.checkIsMutable()
|
||||
return super.removeAll(elements)
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<V>): Boolean {
|
||||
backing.checkIsMutable()
|
||||
return super.retainAll(elements)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: intermediate class with [E] `: Map.Entry<K, V>` is required to support
|
||||
* [contains] for values that are [Map.Entry] but not [MutableMap.MutableEntry],
|
||||
* and probably same for other functions.
|
||||
* This is important because an instance of this class can be used as a result of [Map.entries],
|
||||
* which should support [contains] for [Map.Entry].
|
||||
* For example, this happens when upcasting [MutableMap] to [Map].
|
||||
*
|
||||
* The compiler enables special type-safe barriers to methods like [contains], which has [UnsafeVariance].
|
||||
* Changing type from [MutableMap.MutableEntry] to [E] makes the compiler generate barriers checking that
|
||||
* argument `is` [E] (so technically `is` [Map.Entry]) instead of `is` [MutableMap.MutableEntry].
|
||||
*
|
||||
* See also [KT-42248](https://youtrack.jetbrains.com/issue/KT-42428).
|
||||
*/
|
||||
internal abstract class HashMapEntrySetBase<K, V, E : Map.Entry<K, V>> internal constructor(
|
||||
val backing: InternalMap<K, V>,
|
||||
) : MutableSet<E>, AbstractMutableSet<E>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
override fun contains(element: E): Boolean = backing.containsEntry(element)
|
||||
protected abstract fun getEntry(element: Map.Entry<K, V>): E?
|
||||
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.removeEntry(element)
|
||||
override fun containsAll(elements: Collection<E>): Boolean = backing.containsAllEntries(elements)
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean {
|
||||
backing.checkIsMutable()
|
||||
return super.removeAll(elements)
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<E>): Boolean {
|
||||
backing.checkIsMutable()
|
||||
return super.retainAll(elements)
|
||||
}
|
||||
}
|
||||
|
||||
internal class HashMapEntrySet<K, V> internal constructor(
|
||||
backing: InternalMap<K, V>,
|
||||
) : HashMapEntrySetBase<K, V, MutableMap.MutableEntry<K, V>>(backing) {
|
||||
|
||||
override fun getEntry(element: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>? = backing.getEntry(element)
|
||||
|
||||
override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> = backing.entriesIterator()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2023 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.
|
||||
*/
|
||||
|
||||
@@ -32,9 +32,14 @@ internal interface InternalMap<K, V> {
|
||||
fun valuesIterator(): MutableIterator<V>
|
||||
fun entriesIterator(): MutableIterator<MutableMap.MutableEntry<K, V>>
|
||||
|
||||
fun checkIsMutable() {}
|
||||
fun checkIsMutable()
|
||||
fun build()
|
||||
|
||||
fun containsAllEntries(m: Collection<Map.Entry<*, *>>): Boolean {
|
||||
return m.all(this::containsOtherEntry)
|
||||
return m.all {
|
||||
// entry can be null due to variance.
|
||||
val entry = it.unsafeCast<Any?>()
|
||||
(entry is Map.Entry<*, *>) && containsOtherEntry(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2023 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.
|
||||
*/
|
||||
/*
|
||||
@@ -163,6 +163,13 @@ internal open class InternalStringMap<K, V> : InternalMap<K, V> {
|
||||
values = createJsArray()
|
||||
}
|
||||
|
||||
override fun build() {
|
||||
throw UnsupportedOperationException("build method is not implemented")
|
||||
}
|
||||
|
||||
override fun checkIsMutable() {
|
||||
}
|
||||
|
||||
override fun keysIterator(): MutableIterator<K> = KeysItr(this)
|
||||
override fun valuesIterator(): MutableIterator<V> = ValuesItr(this)
|
||||
override fun entriesIterator(): MutableIterator<MutableEntry<K, V>> = EntriesItr(this)
|
||||
|
||||
@@ -9,177 +9,31 @@
|
||||
*/
|
||||
package kotlin.collections
|
||||
|
||||
import kotlin.collections.MutableMap.MutableEntry
|
||||
|
||||
/**
|
||||
* Hash table based implementation of the [MutableMap] interface, which additionally preserves the insertion order
|
||||
* of entries during the iteration.
|
||||
*
|
||||
* The insertion order is preserved by maintaining a doubly-linked list of all of its entries.
|
||||
* The insertion order is preserved natively by the HashMap implementation.
|
||||
*/
|
||||
public actual open class LinkedHashMap<K, V> : HashMap<K, V>, MutableMap<K, V> {
|
||||
private companion object {
|
||||
private val Empty = LinkedHashMap<Nothing, Nothing>(0).also { it.isReadOnly = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry we use includes next/prev pointers for a doubly-linked circular
|
||||
* list with a head node. This reduces the special cases we have to deal with
|
||||
* in the list operations.
|
||||
|
||||
* Note that we duplicate the key from the underlying hash map so we can find
|
||||
* the eldest entry. The alternative would have been to modify HashMap so more
|
||||
* of the code was directly usable here, but this would have added some
|
||||
* overhead to HashMap, or to reimplement most of the HashMap code here with
|
||||
* small modifications. Paying a small storage cost only if you use
|
||||
* LinkedHashMap and minimizing code size seemed like a better tradeoff
|
||||
*/
|
||||
private inner class ChainEntry<K, V>(key: K, value: V) : AbstractMutableMap.SimpleEntry<K, V>(key, value) {
|
||||
internal var next: ChainEntry<K, V>? = null
|
||||
internal var prev: ChainEntry<K, V>? = null
|
||||
|
||||
override fun setValue(newValue: V): V {
|
||||
this@LinkedHashMap.checkIsMutable()
|
||||
return super.setValue(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class EntrySet : AbstractEntrySet<MutableEntry<K, V>, K, V>() {
|
||||
|
||||
private inner class EntryIterator : MutableIterator<MutableEntry<K, V>> {
|
||||
// The last entry that was returned from this iterator.
|
||||
private var last: ChainEntry<K, V>? = null
|
||||
|
||||
// The next entry to return from this iterator.
|
||||
private var next: ChainEntry<K, V>? = null
|
||||
|
||||
init {
|
||||
next = head
|
||||
// recordLastKnownStructure(map, this)
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return next !== null
|
||||
}
|
||||
|
||||
override fun next(): MutableEntry<K, V> {
|
||||
// checkStructuralChange(map, this)
|
||||
if (!hasNext()) throw NoSuchElementException()
|
||||
|
||||
val current = next!!
|
||||
last = current
|
||||
next = current.next.takeIf { it !== head }
|
||||
return current
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
check(last != null)
|
||||
this@EntrySet.checkIsMutable()
|
||||
// checkStructuralChange(map, this)
|
||||
|
||||
last!!.remove()
|
||||
map.remove(last!!.key)
|
||||
// recordLastKnownStructure(map, this)
|
||||
last = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun add(element: MutableEntry<K, V>): Boolean = throw UnsupportedOperationException("Add is not supported on entries")
|
||||
override fun clear() {
|
||||
this@LinkedHashMap.clear()
|
||||
}
|
||||
|
||||
override fun containsEntry(element: Map.Entry<K, V>): Boolean = this@LinkedHashMap.containsEntry(element)
|
||||
|
||||
override operator fun iterator(): MutableIterator<MutableEntry<K, V>> = EntryIterator()
|
||||
|
||||
override fun removeEntry(element: Map.Entry<K, V>): Boolean {
|
||||
checkIsMutable()
|
||||
if (contains(element)) {
|
||||
this@LinkedHashMap.remove(element.key)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override val size: Int get() = this@LinkedHashMap.size
|
||||
|
||||
override fun checkIsMutable(): Unit = this@LinkedHashMap.checkIsMutable()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The head of the insert order chain, which is a doubly-linked circular
|
||||
* list.
|
||||
*
|
||||
* The most recently inserted node is at the end of the chain, ie.
|
||||
* chain.prev.
|
||||
*/
|
||||
private var head: ChainEntry<K, V>? = null
|
||||
|
||||
/**
|
||||
* Add this node to the end of the chain.
|
||||
*/
|
||||
private fun ChainEntry<K, V>.addToEnd() {
|
||||
// This entry is not in the list.
|
||||
check(next == null && prev == null)
|
||||
|
||||
val _head = head
|
||||
if (_head == null) {
|
||||
head = this
|
||||
next = this
|
||||
prev = this
|
||||
} else {
|
||||
// Chain is valid.
|
||||
val _tail = checkNotNull(_head.prev)
|
||||
// Update me.
|
||||
prev = _tail
|
||||
next = _head
|
||||
// Update my new siblings: current head and old tail
|
||||
_head.prev = this
|
||||
_tail.next = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this node from the chain it is a part of.
|
||||
*/
|
||||
private fun ChainEntry<K, V>.remove() {
|
||||
if (this.next === this) {
|
||||
// if this is single element, remove head
|
||||
head = null
|
||||
} else {
|
||||
if (head === this) {
|
||||
// if this is first element, move head to next
|
||||
head = next
|
||||
}
|
||||
next!!.prev = prev
|
||||
prev!!.next = next
|
||||
}
|
||||
next = null
|
||||
prev = null
|
||||
}
|
||||
|
||||
/*
|
||||
* The hashmap that keeps track of our entries and the chain. Note that we
|
||||
* duplicate the key here to eliminate changes to HashMap and minimize the
|
||||
* code here, at the expense of additional space.
|
||||
*/
|
||||
private val map: HashMap<K, ChainEntry<K, V>>
|
||||
|
||||
private var isReadOnly: Boolean = false
|
||||
|
||||
/**
|
||||
* Creates a new empty [LinkedHashMap].
|
||||
*/
|
||||
actual constructor() : super() {
|
||||
map = HashMap<K, ChainEntry<K, V>>()
|
||||
}
|
||||
actual constructor() : super()
|
||||
|
||||
internal constructor(backingMap: HashMap<K, Any>) : super() {
|
||||
@Suppress("UNCHECKED_CAST") // expected to work due to erasure
|
||||
map = backingMap as HashMap<K, ChainEntry<K, V>>
|
||||
}
|
||||
/**
|
||||
* Creates a new empty [LinkedHashMap] with the specified initial capacity.
|
||||
*
|
||||
* Capacity is the maximum number of entries the map is able to store in current internal data structure.
|
||||
* When the map gets full by a certain default load factor, its capacity is expanded,
|
||||
* which usually leads to rebuild of the internal data structure.
|
||||
*
|
||||
* @param initialCapacity the initial capacity of the created map.
|
||||
* Note that the argument is just a hint for the implementation and can be ignored.
|
||||
*
|
||||
* @throws IllegalArgumentException if [initialCapacity] is negative.
|
||||
*/
|
||||
actual constructor(initialCapacity: Int) : super(initialCapacity)
|
||||
|
||||
/**
|
||||
* Creates a new empty [LinkedHashMap] with the specified initial capacity and load factor.
|
||||
@@ -195,102 +49,28 @@ public actual open class LinkedHashMap<K, V> : HashMap<K, V>, MutableMap<K, V> {
|
||||
*
|
||||
* @throws IllegalArgumentException if [initialCapacity] is negative or [loadFactor] is non-positive.
|
||||
*/
|
||||
actual constructor(initialCapacity: Int, loadFactor: Float) : super(initialCapacity, loadFactor) {
|
||||
map = HashMap<K, ChainEntry<K, V>>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty [LinkedHashMap] with the specified initial capacity.
|
||||
*
|
||||
* Capacity is the maximum number of entries the map is able to store in current internal data structure.
|
||||
* When the map gets full by a certain default load factor, its capacity is expanded,
|
||||
* which usually leads to rebuild of the internal data structure.
|
||||
*
|
||||
* @param initialCapacity the initial capacity of the created map.
|
||||
* Note that the argument is just a hint for the implementation and can be ignored.
|
||||
*
|
||||
* @throws IllegalArgumentException if [initialCapacity] is negative.
|
||||
*/
|
||||
actual constructor(initialCapacity: Int) : this(initialCapacity, 1.0f)
|
||||
actual constructor(initialCapacity: Int, loadFactor: Float) : super(initialCapacity, loadFactor)
|
||||
|
||||
/**
|
||||
* Creates a new [LinkedHashMap] filled with the contents of the specified [original] map.
|
||||
*
|
||||
* The iteration order of entries in the created map is the same as in the [original] map.
|
||||
*/
|
||||
actual constructor(original: Map<out K, V>) {
|
||||
map = HashMap<K, ChainEntry<K, V>>()
|
||||
this.putAll(original)
|
||||
actual constructor(original: Map<out K, V>) : super(original)
|
||||
|
||||
internal constructor(internalMap: InternalMap<K, V>) : super(internalMap)
|
||||
|
||||
private object EmptyHolder {
|
||||
val value = LinkedHashMap(InternalHashMap<Nothing, Nothing>(0).also { it.build() })
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun build(): Map<K, V> {
|
||||
checkIsMutable()
|
||||
isReadOnly = true
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return if (size > 0) this else (Empty as Map<K, V>)
|
||||
internalMap.build()
|
||||
return if (size > 0) this else EmptyHolder.value.unsafeCast<Map<K, V>>()
|
||||
}
|
||||
|
||||
actual override fun clear() {
|
||||
checkIsMutable()
|
||||
map.clear()
|
||||
head = null
|
||||
}
|
||||
|
||||
|
||||
// override fun clone(): Any {
|
||||
// return LinkedHashMap(this)
|
||||
// }
|
||||
|
||||
|
||||
actual override fun containsKey(key: K): Boolean = map.containsKey(key)
|
||||
|
||||
actual override fun containsValue(value: V): Boolean {
|
||||
var node: ChainEntry<K, V> = head ?: return false
|
||||
do {
|
||||
if (node.value == value) {
|
||||
return true
|
||||
}
|
||||
node = node.next!!
|
||||
} while (node !== head)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
internal override fun createEntrySet(): MutableSet<MutableMap.MutableEntry<K, V>> = EntrySet()
|
||||
|
||||
actual override operator fun get(key: K): V? = map.get(key)?.value
|
||||
|
||||
actual override fun put(key: K, value: V): V? {
|
||||
checkIsMutable()
|
||||
|
||||
val old = map.get(key)
|
||||
if (old == null) {
|
||||
val newEntry = ChainEntry(key, value)
|
||||
map.put(key, newEntry)
|
||||
newEntry.addToEnd()
|
||||
return null
|
||||
} else {
|
||||
return old.setValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
actual override fun remove(key: K): V? {
|
||||
checkIsMutable()
|
||||
|
||||
val entry = map.remove(key)
|
||||
if (entry != null) {
|
||||
entry.remove()
|
||||
return entry.value
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
actual override val size: Int get() = map.size
|
||||
|
||||
internal override fun checkIsMutable() {
|
||||
if (isReadOnly) throw UnsupportedOperationException()
|
||||
}
|
||||
override fun checkIsMutable() = internalMap.checkIsMutable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,5 +78,5 @@ public actual open class LinkedHashMap<K, V> : HashMap<K, V>, MutableMap<K, V> {
|
||||
* JS object without hashing them.
|
||||
*/
|
||||
public fun <V> linkedStringMapOf(vararg pairs: Pair<String, V>): LinkedHashMap<String, V> {
|
||||
return LinkedHashMap<String, V>(stringMapOf<Any>()).apply { putAll(pairs) }
|
||||
return LinkedHashMap<String, V>(InternalStringMap()).apply { putAll(pairs) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user