diff --git a/libraries/stdlib/js/src/kotlin/collections/AbstractMutableMap.kt b/libraries/stdlib/js/src/kotlin/collections/AbstractMutableMap.kt index 0b1b05ffee6..36cbaeaa421 100644 --- a/libraries/stdlib/js/src/kotlin/collections/AbstractMutableMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/AbstractMutableMap.kt @@ -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 protected actual constructor() : AbstractMap(), MutableMap { - /** - * A mutable [Map.Entry] shared by several [Map] implementations. - */ - internal open class SimpleEntry(override val key: K, value: V) : MutableMap.MutableEntry { - constructor(entry: Map.Entry) : this(entry.key, entry.value) + internal open fun createKeysView(): MutableSet = HashMapKeysDefault(this) + internal open fun createValuesView(): MutableCollection = HashMapValuesDefault(this) - private var _value = value + private var keysView: MutableSet? = null + private var valuesView: MutableCollection? = null - override val value: V get() = _value + actual override val keys: MutableSet + 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, K, V> : AbstractMutableSet() { - final override fun contains(element: E): Boolean = containsEntry(element) - abstract fun containsEntry(element: Map.Entry): Boolean - final override fun remove(element: E): Boolean = removeEntry(element) - abstract fun removeEntry(element: Map.Entry): Boolean - } + actual override val values: MutableCollection + get() = valuesView ?: createValuesView().also { valuesView = it } actual override fun clear() { entries.clear() } - private var _keys: MutableSet? = null - actual override val keys: MutableSet - get() { - if (_keys == null) { - _keys = object : AbstractMutableSet() { - 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 { - val entryIterator = entries.iterator() - return object : MutableIterator { - 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) { @@ -105,33 +45,6 @@ public actual abstract class AbstractMutableMap protected actual construct } } - private var _values: MutableCollection? = null - actual override val values: MutableCollection - get() { - if (_values == null) { - _values = object : AbstractMutableCollection() { - 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 { - val entryIterator = entries.iterator() - return object : MutableIterator { - 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 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() {} } diff --git a/libraries/stdlib/js/src/kotlin/collections/HashMap.kt b/libraries/stdlib/js/src/kotlin/collections/HashMap.kt index 1c1c1311ed8..c0e48f98441 100644 --- a/libraries/stdlib/js/src/kotlin/collections/HashMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/HashMap.kt @@ -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 : AbstractMutableMap, MutableMap { - - private inner class EntrySet : AbstractEntrySet, K, V>() { - - override fun add(element: MutableEntry): Boolean = throw UnsupportedOperationException("Add is not supported on entries") - override fun clear() { - this@HashMap.clear() - } - - override fun containsEntry(element: Map.Entry): Boolean = this@HashMap.containsEntry(element) - - override operator fun iterator(): MutableIterator> = internalMap.entriesIterator() - - override fun removeEntry(element: Map.Entry): 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 + internal val internalMap: InternalMap internal constructor(internalMap: InternalMap) : super() { this.internalMap = internalMap @@ -92,7 +68,6 @@ public actual open class HashMap : AbstractMutableMap, MutableMap : AbstractMutableMap, MutableMap>? = null - actual override val entries: MutableSet> - get() { - if (_entries == null) { - _entries = createEntrySet() - } - return _entries!! - } + override fun createKeysView(): MutableSet = HashMapKeys(internalMap) + override fun createValuesView(): MutableCollection = HashMapValues(internalMap) - internal open fun createEntrySet(): MutableSet> = EntrySet() + private var entriesView: HashMapEntrySet? = null + actual override val entries: MutableSet> + 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 : AbstractMutableMap, MutableMap) = internalMap.putAll(from) } /** diff --git a/libraries/stdlib/js/src/kotlin/collections/HashMapEntry.kt b/libraries/stdlib/js/src/kotlin/collections/HashMapEntry.kt new file mode 100644 index 00000000000..767ae3b40e0 --- /dev/null +++ b/libraries/stdlib/js/src/kotlin/collections/HashMapEntry.kt @@ -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 internal constructor( + private val backing: InternalMap, +) : MutableSet, AbstractMutableSet() { + + 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): Boolean = throw UnsupportedOperationException() + override fun remove(element: E): Boolean = backing.remove(element) != null + override fun iterator(): MutableIterator = backing.keysIterator() + + override fun checkIsMutable() = backing.checkIsMutable() +} + +internal class HashMapValues internal constructor( + private val backing: InternalMap<*, V>, +) : MutableCollection, AbstractMutableCollection() { + + 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): Boolean = throw UnsupportedOperationException() + override fun clear() = backing.clear() + override fun iterator(): MutableIterator = backing.valuesIterator() + override fun remove(element: V): Boolean = backing.removeValue(element) + + override fun checkIsMutable() = backing.checkIsMutable() +} + +/** + * Note: intermediate class with [E] `: Map.Entry` 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> internal constructor( + val backing: InternalMap, +) : MutableSet, AbstractMutableSet() { + + 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): E? + override fun clear() = backing.clear() + override fun add(element: E): Boolean = throw UnsupportedOperationException() + override fun addAll(elements: Collection): Boolean = throw UnsupportedOperationException() + override fun remove(element: E): Boolean = backing.removeEntry(element) + override fun containsAll(elements: Collection): Boolean = backing.containsAllEntries(elements) + + override fun checkIsMutable() = backing.checkIsMutable() +} + +internal class HashMapEntrySet internal constructor( + backing: InternalMap, +) : HashMapEntrySetBase>(backing) { + + override fun getEntry(element: Map.Entry): MutableMap.MutableEntry? = backing.getEntry(element) + + override fun iterator(): MutableIterator> = backing.entriesIterator() +} diff --git a/libraries/stdlib/js/src/kotlin/collections/HashMapEntryDefault.kt b/libraries/stdlib/js/src/kotlin/collections/HashMapEntryDefault.kt new file mode 100644 index 00000000000..79c1ed3a8d1 --- /dev/null +++ b/libraries/stdlib/js/src/kotlin/collections/HashMapEntryDefault.kt @@ -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(private val backingMap: AbstractMutableMap) : AbstractMutableSet() { + 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 { + val entryIterator = backingMap.entries.iterator() + return object : MutableIterator { + 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(private val backingMap: AbstractMutableMap) : AbstractMutableCollection() { + 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 { + val entryIterator = backingMap.entries.iterator() + return object : MutableIterator { + 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() +} diff --git a/libraries/stdlib/js/src/kotlin/collections/InternalHashMap.kt b/libraries/stdlib/js/src/kotlin/collections/InternalHashMap.kt index 2555531e30e..57e209c45f6 100644 --- a/libraries/stdlib/js/src/kotlin/collections/InternalHashMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/InternalHashMap.kt @@ -19,10 +19,6 @@ internal class InternalHashMap private constructor( override val size: Int get() = _size - private var keysView: HashMapKeys? = null - private var valuesView: HashMapValues? = null - private var entriesView: HashMapEntrySet? = null - private var isReadOnly: Boolean = false // ---------------------------- functions ---------------------------- @@ -78,11 +74,9 @@ internal class InternalHashMap private constructor( require(loadFactor > 0) { "Non-positive load factor: $loadFactor" } } - @PublishedApi - internal fun build(): InternalHashMap { + 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 private constructor( length = 0 } - val keys: MutableSet - get() { - val cur = keysView - return if (cur == null) { - val new = HashMapKeys(this) - keysView = new - new - } else cur - } - - val values: MutableCollection - get() { - val cur = valuesView - return if (cur == null) { - val new = HashMapValues(this) - valuesView = new - new - } else cur - } - - val entries: MutableSet> - 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 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 private constructor( private fun computeShift(hashSize: Int): Int = hashSize.countLeadingZeroBits() + 1 } - internal object EmptyHolder { - val value_ = InternalHashMap(0).also { it.isReadOnly = true } - - fun value(): InternalHashMap { - @Suppress("UNCHECKED_CAST") - return value_ as InternalHashMap - } - } - internal open class Itr( internal val map: InternalHashMap, ) { @@ -622,99 +577,3 @@ internal class InternalHashMap private constructor( override fun toString(): String = "$key=$value" } } - -internal class HashMapKeys internal constructor( - private val backing: InternalMap, -) : MutableSet, AbstractMutableSet() { - - 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): Boolean = throw UnsupportedOperationException() - override fun remove(element: E): Boolean = backing.remove(element) != null - override fun iterator(): MutableIterator = backing.keysIterator() - - override fun removeAll(elements: Collection): Boolean { - backing.checkIsMutable() - return super.removeAll(elements) - } - - override fun retainAll(elements: Collection): Boolean { - backing.checkIsMutable() - return super.retainAll(elements) - } -} - -internal class HashMapValues internal constructor( - private val backing: InternalMap<*, V>, -) : MutableCollection, AbstractMutableCollection() { - - 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): Boolean = throw UnsupportedOperationException() - override fun clear() = backing.clear() - override fun iterator(): MutableIterator = backing.valuesIterator() - override fun remove(element: V): Boolean = backing.removeValue(element) - - override fun removeAll(elements: Collection): Boolean { - backing.checkIsMutable() - return super.removeAll(elements) - } - - override fun retainAll(elements: Collection): Boolean { - backing.checkIsMutable() - return super.retainAll(elements) - } -} - -/** - * Note: intermediate class with [E] `: Map.Entry` 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> internal constructor( - val backing: InternalMap, -) : MutableSet, AbstractMutableSet() { - - 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): E? - override fun clear() = backing.clear() - override fun add(element: E): Boolean = throw UnsupportedOperationException() - override fun addAll(elements: Collection): Boolean = throw UnsupportedOperationException() - override fun remove(element: E): Boolean = backing.removeEntry(element) - override fun containsAll(elements: Collection): Boolean = backing.containsAllEntries(elements) - - override fun removeAll(elements: Collection): Boolean { - backing.checkIsMutable() - return super.removeAll(elements) - } - - override fun retainAll(elements: Collection): Boolean { - backing.checkIsMutable() - return super.retainAll(elements) - } -} - -internal class HashMapEntrySet internal constructor( - backing: InternalMap, -) : HashMapEntrySetBase>(backing) { - - override fun getEntry(element: Map.Entry): MutableMap.MutableEntry? = backing.getEntry(element) - - override fun iterator(): MutableIterator> = backing.entriesIterator() -} diff --git a/libraries/stdlib/js/src/kotlin/collections/InternalMap.kt b/libraries/stdlib/js/src/kotlin/collections/InternalMap.kt index e60fee65d06..e12cefa9b23 100644 --- a/libraries/stdlib/js/src/kotlin/collections/InternalMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/InternalMap.kt @@ -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 { fun valuesIterator(): MutableIterator fun entriesIterator(): MutableIterator> - fun checkIsMutable() {} + fun checkIsMutable() + fun build() fun containsAllEntries(m: Collection>): Boolean { - return m.all(this::containsOtherEntry) + return m.all { + // entry can be null due to variance. + val entry = it.unsafeCast() + (entry is Map.Entry<*, *>) && containsOtherEntry(entry) + } } } diff --git a/libraries/stdlib/js/src/kotlin/collections/InternalStringMap.kt b/libraries/stdlib/js/src/kotlin/collections/InternalStringMap.kt index 78cd153496c..85e84782fe5 100644 --- a/libraries/stdlib/js/src/kotlin/collections/InternalStringMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/InternalStringMap.kt @@ -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 : InternalMap { values = createJsArray() } + override fun build() { + throw UnsupportedOperationException("build method is not implemented") + } + + override fun checkIsMutable() { + } + override fun keysIterator(): MutableIterator = KeysItr(this) override fun valuesIterator(): MutableIterator = ValuesItr(this) override fun entriesIterator(): MutableIterator> = EntriesItr(this) diff --git a/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt b/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt index b44ef325ac3..92b2d2de426 100644 --- a/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt @@ -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 : HashMap, MutableMap { - private companion object { - private val Empty = LinkedHashMap(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(key: K, value: V) : AbstractMutableMap.SimpleEntry(key, value) { - internal var next: ChainEntry? = null - internal var prev: ChainEntry? = null - - override fun setValue(newValue: V): V { - this@LinkedHashMap.checkIsMutable() - return super.setValue(newValue) - } - } - - private inner class EntrySet : AbstractEntrySet, K, V>() { - - private inner class EntryIterator : MutableIterator> { - // The last entry that was returned from this iterator. - private var last: ChainEntry? = null - - // The next entry to return from this iterator. - private var next: ChainEntry? = null - - init { - next = head -// recordLastKnownStructure(map, this) - } - - override fun hasNext(): Boolean { - return next !== null - } - - override fun next(): MutableEntry { -// 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): Boolean = throw UnsupportedOperationException("Add is not supported on entries") - override fun clear() { - this@LinkedHashMap.clear() - } - - override fun containsEntry(element: Map.Entry): Boolean = this@LinkedHashMap.containsEntry(element) - - override operator fun iterator(): MutableIterator> = EntryIterator() - - override fun removeEntry(element: Map.Entry): 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? = null - - /** - * Add this node to the end of the chain. - */ - private fun ChainEntry.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.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> - - private var isReadOnly: Boolean = false - /** * Creates a new empty [LinkedHashMap]. */ - actual constructor() : super() { - map = HashMap>() - } + actual constructor() : super() - internal constructor(backingMap: HashMap) : super() { - @Suppress("UNCHECKED_CAST") // expected to work due to erasure - map = backingMap as HashMap> - } + /** + * 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 : HashMap, MutableMap { * * @throws IllegalArgumentException if [initialCapacity] is negative or [loadFactor] is non-positive. */ - actual constructor(initialCapacity: Int, loadFactor: Float) : super(initialCapacity, loadFactor) { - map = HashMap>() - } - - /** - * 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) { - map = HashMap>() - this.putAll(original) + actual constructor(original: Map) : super(original) + + internal constructor(internalMap: InternalMap) : super(internalMap) + + private object EmptyHolder { + val value = LinkedHashMap(InternalHashMap(0).also { it.build() }) } @PublishedApi internal fun build(): Map { - checkIsMutable() - isReadOnly = true - @Suppress("UNCHECKED_CAST") - return if (size > 0) this else (Empty as Map) + internalMap.build() + return if (size > 0) this else EmptyHolder.value.unsafeCast>() } - 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 = head ?: return false - do { - if (node.value == value) { - return true - } - node = node.next!! - } while (node !== head) - return false - } - - - internal override fun createEntrySet(): MutableSet> = 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 : HashMap, MutableMap { * JS object without hashing them. */ public fun linkedStringMapOf(vararg pairs: Pair): LinkedHashMap { - return LinkedHashMap(stringMapOf()).apply { putAll(pairs) } + return LinkedHashMap(InternalStringMap()).apply { putAll(pairs) } }