Import AbstractHashMap, HashMap, HashSet, LinkedHashMap, LinkedHashSet from GWT
#KT-12386
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Based on GWT AbstractHashMap
|
||||
* Copyright 2008 Google Inc.
|
||||
*/
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
abstract class AbstractHashMap<K, V> : AbstractMap<K, V> {
|
||||
|
||||
private inner class EntrySet : AbstractSet<Entry<K, V>>() {
|
||||
|
||||
override fun clear() {
|
||||
this@AbstractHashMap.clear()
|
||||
}
|
||||
|
||||
override operator fun contains(o: Any?): Boolean {
|
||||
if (o is Entry<*, *>) {
|
||||
return containsEntry(o as Entry<*, *>?)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override operator fun iterator(): Iterator<Entry<K, V>> {
|
||||
return EntrySetIterator()
|
||||
}
|
||||
|
||||
override fun remove(entry: Any?): Boolean {
|
||||
if (contains(entry)) {
|
||||
val key = (entry as Entry<*, *>).key
|
||||
this@AbstractHashMap.remove(key)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return this@AbstractHashMap.size
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator for `EntrySet`.
|
||||
*/
|
||||
private inner class EntrySetIterator : Iterator<Entry<K, V>> {
|
||||
private val stringMapEntries = stringMap!!.iterator()
|
||||
private var current: MutableIterator<Entry<K, V>> = stringMapEntries
|
||||
private var last: MutableIterator<Entry<K, V>>? = null
|
||||
private var hasNext = computeHasNext()
|
||||
|
||||
init {
|
||||
recordLastKnownStructure(this@AbstractHashMap, this)
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return hasNext
|
||||
}
|
||||
|
||||
private fun computeHasNext(): Boolean {
|
||||
if (current.hasNext()) {
|
||||
return true
|
||||
}
|
||||
if (current !== stringMapEntries) {
|
||||
return false
|
||||
}
|
||||
current = hashCodeMap!!.iterator()
|
||||
return current.hasNext()
|
||||
}
|
||||
|
||||
override fun next(): Entry<K, V> {
|
||||
checkStructuralChange(this@AbstractHashMap, this)
|
||||
checkElement(hasNext())
|
||||
|
||||
last = current
|
||||
val rv = current.next()
|
||||
hasNext = computeHasNext()
|
||||
|
||||
return rv
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
checkState(last != null)
|
||||
checkStructuralChange(this@AbstractHashMap, this)
|
||||
|
||||
last!!.remove()
|
||||
last = null
|
||||
hasNext = computeHasNext()
|
||||
|
||||
recordLastKnownStructure(this@AbstractHashMap, this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of integral hashCodes onto entries.
|
||||
*/
|
||||
@Transient private var hashCodeMap: InternalHashCodeMap<K, V>? = null
|
||||
|
||||
/**
|
||||
* A map of Strings onto values.
|
||||
*/
|
||||
@Transient private var stringMap: InternalStringMap<K, V>? = null
|
||||
|
||||
constructor() {
|
||||
reset()
|
||||
}
|
||||
|
||||
@JvmOverloads constructor(ignored: Int, alsoIgnored: Float = 0f) {
|
||||
// This implementation of HashMap has no need of load factors or capacities.
|
||||
checkArgument(ignored >= 0, "Negative initial capacity")
|
||||
checkArgument(alsoIgnored >= 0, "Non-positive load factor")
|
||||
|
||||
reset()
|
||||
}
|
||||
|
||||
constructor(toBeCopied: Map<out K, V>) {
|
||||
reset()
|
||||
this.putAll(toBeCopied)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
reset()
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
hashCodeMap = InternalHashCodeMap<K, V>(this)
|
||||
stringMap = InternalStringMap<K, V>(this)
|
||||
structureChanged(this)
|
||||
}
|
||||
|
||||
@SpecializeMethod(params = { String.class }, target = "hasStringValue")
|
||||
override fun containsKey(key: Any?): Boolean {
|
||||
return if (key is String)
|
||||
hasStringValue(JsUtils.unsafeCastToString(key))
|
||||
else
|
||||
hasHashValue(key)
|
||||
}
|
||||
|
||||
override fun containsValue(value: Any?): Boolean {
|
||||
return containsValue(value, stringMap) || containsValue(value, hashCodeMap)
|
||||
}
|
||||
|
||||
private fun containsValue(value: Any, entries: Iterable<Entry<K, V>>): Boolean {
|
||||
for (entry in entries) {
|
||||
if (equals(value, entry.value)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun entrySet(): Set<Entry<K, V>> {
|
||||
return EntrySet()
|
||||
}
|
||||
|
||||
@SpecializeMethod(params = { String.class }, target = "getStringValue")
|
||||
override operator fun get(key: Any?): V {
|
||||
return if (key is String)
|
||||
getStringValue(JsUtils.unsafeCastToString(key))
|
||||
else
|
||||
getHashValue(key)
|
||||
}
|
||||
|
||||
@SpecializeMethod(params = { String.class, Object .class }, target = "putStringValue")
|
||||
override fun put(key: K?, value: V?): V {
|
||||
return if (key is String)
|
||||
putStringValue(JsUtils.unsafeCastToString(key), value)
|
||||
else
|
||||
putHashValue(key, value)
|
||||
}
|
||||
|
||||
@SpecializeMethod(params = { String.class }, target = "removeStringValue")
|
||||
override fun remove(key: Any?): V {
|
||||
return if (key is String)
|
||||
removeStringValue(JsUtils.unsafeCastToString(key))
|
||||
else
|
||||
removeHashValue(key)
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return hashCodeMap!!.size() + stringMap!!.size()
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must override to return a whether or not two keys or values are
|
||||
* equal.
|
||||
*/
|
||||
internal abstract fun equals(value1: Any, value2: Any): Boolean
|
||||
|
||||
/**
|
||||
* Subclasses must override to return a hash code for a given key. The key is
|
||||
* guaranteed to be non-null and not a String.
|
||||
*/
|
||||
internal abstract fun getHashCode(key: Any): Int
|
||||
|
||||
/**
|
||||
* Returns the Map.Entry whose key is Object equal to `key`,
|
||||
* provided that `key`'s hash code is `hashCode`;
|
||||
* or `null` if no such Map.Entry exists at the specified
|
||||
* hashCode.
|
||||
*/
|
||||
private fun getHashValue(key: Any?): V {
|
||||
return getEntryValueOrNull(hashCodeMap!!.getEntry(key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the given key in the stringMap. Returns
|
||||
* `null` if the specified key does not exist.
|
||||
*/
|
||||
private fun getStringValue(key: String?): V {
|
||||
return if (key == null) getHashValue(null) else stringMap!!.get(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the a key exists in the hashCodeMap that is Object equal to
|
||||
* `key`, provided that `key`'s hash code is
|
||||
* `hashCode`.
|
||||
*/
|
||||
private fun hasHashValue(key: Any?): Boolean {
|
||||
return hashCodeMap!!.getEntry(key) != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given key exists in the stringMap.
|
||||
*/
|
||||
private fun hasStringValue(key: String?): Boolean {
|
||||
return if (key == null) hasHashValue(null) else stringMap!!.contains(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified key to the specified value in the hashCodeMap. Returns
|
||||
* the value previously at that key. Returns `null` if the
|
||||
* specified key did not exist.
|
||||
*/
|
||||
private fun putHashValue(key: K?, value: V): V {
|
||||
return hashCodeMap!!.put(key, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified key to the specified value in the stringMap. Returns the
|
||||
* value previously at that key. Returns `null` if the specified
|
||||
* key did not exist.
|
||||
*/
|
||||
private fun putStringValue(key: String?, value: V): V {
|
||||
return if (key == null) putHashValue(null, value) else stringMap!!.put(key, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the pair whose key is Object equal to `key` from
|
||||
* `hashCodeMap`, provided that `key`'s hash code
|
||||
* is `hashCode`. Returns the value that was associated with the
|
||||
* removed key, or null if no such key existed.
|
||||
*/
|
||||
private fun removeHashValue(key: Any?): V {
|
||||
return hashCodeMap!!.remove(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified key from the stringMap and returns the value that was
|
||||
* previously there. Returns `null` if the specified key does not
|
||||
* exist.
|
||||
*/
|
||||
private fun removeStringValue(key: String?): V {
|
||||
return if (key == null) removeHashValue(null) else stringMap!!.remove(key)
|
||||
}
|
||||
}// This implementation of HashMap has no need of initial capacities.
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Based on GWT HashMap
|
||||
* Copyright 2008 Google Inc.
|
||||
*/
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
open class HashMap<K, V> : AbstractHashMap<K, V>, Cloneable, Serializable {
|
||||
|
||||
/**
|
||||
* Ensures that RPC will consider type parameter K to be exposed. It will be
|
||||
* pruned by dead code elimination.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private val exposeKey: K? = null
|
||||
|
||||
/**
|
||||
* Ensures that RPC will consider type parameter V to be exposed. It will be
|
||||
* pruned by dead code elimination.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private val exposeValue: V? = null
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
constructor(ignored: Int) : super(ignored) {
|
||||
}
|
||||
|
||||
constructor(ignored: Int, alsoIgnored: Float) : super(ignored, alsoIgnored) {
|
||||
}
|
||||
|
||||
constructor(toBeCopied: Map<out K, V>) : super(toBeCopied) {
|
||||
}
|
||||
|
||||
public override fun clone(): Any {
|
||||
return HashMap<K, V>(this)
|
||||
}
|
||||
|
||||
internal fun equals(value1: Any, value2: Any): Boolean {
|
||||
return value1 == value2
|
||||
}
|
||||
|
||||
internal fun getHashCode(key: Any): Int {
|
||||
val hashCode = key.hashCode()
|
||||
// Coerce to int -- our classes all do this, but a user-written class might not.
|
||||
return ensureInt(hashCode)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Based on GWT HashSet
|
||||
* Copyright 2008 Google Inc.
|
||||
*/
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
|
||||
/**
|
||||
* Implements a set in terms of a hash table. [[Sun
|
||||
* docs]](http://java.sun.com/j2se/1.5.0/docs/api/java/util/HashSet.html)
|
||||
|
||||
* @param element type.
|
||||
*/
|
||||
open class HashSet<E> : AbstractSet<E>, Set<E>, Cloneable, Serializable {
|
||||
|
||||
@Transient private var map: HashMap<E, Any>? = null
|
||||
|
||||
/**
|
||||
* Ensures that RPC will consider type parameter E to be exposed. It will be
|
||||
* pruned by dead code elimination.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private val exposeElement: E? = null
|
||||
|
||||
constructor() {
|
||||
map = HashMap<E, Any>()
|
||||
}
|
||||
|
||||
constructor(c: Collection<E>) {
|
||||
map = HashMap<E, Any>(c.size)
|
||||
addAll(c)
|
||||
}
|
||||
|
||||
constructor(initialCapacity: Int) {
|
||||
map = HashMap<E, Any>(initialCapacity)
|
||||
}
|
||||
|
||||
constructor(initialCapacity: Int, loadFactor: Float) {
|
||||
map = HashMap<E, Any>(initialCapacity, loadFactor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected constructor to specify the underlying map. This is used by
|
||||
* LinkedHashSet.
|
||||
|
||||
* @param map underlying map to use.
|
||||
*/
|
||||
protected constructor(map: HashMap<E, Any>) {
|
||||
this.map = map
|
||||
}
|
||||
|
||||
override fun add(o: E?): Boolean {
|
||||
val old = map!!.put(o, this)
|
||||
return old == null
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
map!!.clear()
|
||||
}
|
||||
|
||||
public override fun clone(): Any {
|
||||
return HashSet<E>(this)
|
||||
}
|
||||
|
||||
override operator fun contains(o: Any?): Boolean {
|
||||
return map!!.containsKey(o)
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return map!!.isEmpty()
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<E> {
|
||||
return map!!.keys.iterator()
|
||||
}
|
||||
|
||||
override fun remove(o: Any?): Boolean {
|
||||
return map!!.remove(o) != null
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return map!!.size
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Based on GWT InternalHashCodeMap
|
||||
* Copyright 2008 Google Inc.
|
||||
*/
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
import java.util.ConcurrentModificationDetector.structureChanged
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry
|
||||
|
||||
import javaemul.internal.ArrayHelper
|
||||
|
||||
/**
|
||||
* A simple wrapper around JavaScriptObject to provide [java.util.Map]-like semantics for any
|
||||
* key type.
|
||||
*
|
||||
*
|
||||
* Implementation notes:
|
||||
*
|
||||
*
|
||||
* A key's hashCode is the index in backingMap which should contain that key. Since several keys may
|
||||
* have the same hash, each value in hashCodeMap is actually an array containing all entries whose
|
||||
* keys share the same hash.
|
||||
*/
|
||||
private class InternalHashCodeMap<K, V>(private val host: AbstractHashMap<K, V>) : Iterable<Entry<K, V>> {
|
||||
|
||||
private val backingMap = InternalJsMapFactory.newJsMap()
|
||||
private var size: Int = 0
|
||||
|
||||
fun put(key: K, value: V): V? {
|
||||
val hashCode = hash(key)
|
||||
val chain = getChainOrEmpty(hashCode)
|
||||
|
||||
if (chain.size == 0) {
|
||||
// This is a new chain, put it to the map.
|
||||
backingMap.set(hashCode, chain)
|
||||
}
|
||||
else {
|
||||
// Chain already exists, perhaps key also exists.
|
||||
val entry = findEntryInChain(key, chain)
|
||||
if (entry != null) {
|
||||
return entry!!.setValue(value)
|
||||
}
|
||||
}
|
||||
chain[chain.size] = SimpleEntry<K, V>(key, value)
|
||||
size++
|
||||
structureChanged(host)
|
||||
return null
|
||||
}
|
||||
|
||||
fun remove(key: Any): V? {
|
||||
val hashCode = hash(key)
|
||||
val chain = getChainOrEmpty(hashCode)
|
||||
for (i in chain.indices) {
|
||||
val entry = chain[i]
|
||||
if (host.equals(key, entry.key)) {
|
||||
if (chain.size == 1) {
|
||||
ArrayHelper.setLength(chain, 0)
|
||||
// remove the whole array
|
||||
backingMap.delete(hashCode)
|
||||
}
|
||||
else {
|
||||
// splice out the entry we're removing
|
||||
ArrayHelper.removeFrom(chain, i, 1)
|
||||
}
|
||||
size--
|
||||
structureChanged(host)
|
||||
return entry.value
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getEntry(key: Any): Entry<K, V> {
|
||||
return findEntryInChain(key, getChainOrEmpty(hash(key)))
|
||||
}
|
||||
|
||||
private fun findEntryInChain(key: Any, chain: Array<Entry<K, V>>): Entry<K, V>? {
|
||||
for (entry in chain) {
|
||||
if (host.equals(key, entry.key)) {
|
||||
return entry
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun size(): Int {
|
||||
return size
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<Entry<K, V>> {
|
||||
return object : Iterator<Entry<K, V>> {
|
||||
internal val chains = backingMap.entries()
|
||||
internal var itemIndex = 0
|
||||
internal var chain = newEntryChain()
|
||||
internal var lastEntry: Entry<K, V>? = null
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
if (itemIndex < chain.size) {
|
||||
return true
|
||||
}
|
||||
val current = chains.next()
|
||||
if (!current.done) {
|
||||
// Move to the beginning of next chain
|
||||
chain = unsafeCastToArray(current.getValue())
|
||||
itemIndex = 0
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun next(): Entry<K, V> {
|
||||
lastEntry = chain[itemIndex++]
|
||||
return lastEntry
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
this@InternalHashCodeMap.remove(lastEntry!!.key)
|
||||
// Unless we are in a new chain, all items have shifted so our itemIndex should as well...
|
||||
if (itemIndex != 0) {
|
||||
itemIndex--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getChainOrEmpty(hashCode: Int): Array<Entry<K, V>> {
|
||||
val chain = unsafeCastToArray(backingMap.get(hashCode))
|
||||
return chain ?: newEntryChain()
|
||||
}
|
||||
|
||||
private fun newEntryChain(/*-{
|
||||
return [];
|
||||
}-*/): Array<Entry<K, V>>
|
||||
|
||||
private fun unsafeCastToArray(arr: Any /*-{
|
||||
return arr;
|
||||
}-*/): Array<Entry<K, V>>?
|
||||
|
||||
/**
|
||||
* Returns hash code of the key as calculated by [AbstractHashMap.getHashCode] but
|
||||
* also handles null keys as well.
|
||||
*/
|
||||
private fun hash(key: Any?): Int {
|
||||
return if (key == null) 0 else host.getHashCode(key)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Based on GWT LinkedHashMap
|
||||
* Copyright 2008 Google Inc.
|
||||
*/
|
||||
package kotlin.collections
|
||||
|
||||
open class LinkedHashMap<K, V> : HashMap<K, V>, Map<K, V> {
|
||||
|
||||
/**
|
||||
* 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 @JvmOverloads constructor(key: K? = null, value: V? = null) : AbstractMap.SimpleEntry<K, V>(key, value) {
|
||||
@Transient private var next: ChainEntry? = null
|
||||
@Transient private var prev: ChainEntry? = null
|
||||
|
||||
/**
|
||||
* Add this node to the end of the chain.
|
||||
*/
|
||||
fun addToEnd() {
|
||||
val tail = head.prev
|
||||
|
||||
// Chain is valid.
|
||||
assert(head != null && tail != null)
|
||||
|
||||
// This entry is not in the list.
|
||||
assert(next == null && prev == null)
|
||||
|
||||
// Update me.
|
||||
prev = tail
|
||||
next = head
|
||||
tail!!.next = head.prev = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this node from any list it may be a part of.
|
||||
*/
|
||||
fun remove() {
|
||||
next!!.prev = prev
|
||||
prev!!.next = next
|
||||
next = prev = null
|
||||
}
|
||||
}
|
||||
|
||||
private inner class EntrySet : AbstractSet<Entry<K, V>>() {
|
||||
|
||||
private inner class EntryIterator : Iterator<Entry<K, V>> {
|
||||
// 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.next
|
||||
recordLastKnownStructure(map, this)
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return next !== head
|
||||
}
|
||||
|
||||
override fun next(): Entry<K, V> {
|
||||
checkStructuralChange(map, this)
|
||||
checkCriticalElement(hasNext())
|
||||
|
||||
last = next
|
||||
next = next!!.next
|
||||
return last
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
checkState(last != null)
|
||||
checkStructuralChange(map, this)
|
||||
|
||||
last!!.remove()
|
||||
map.remove(last!!.key)
|
||||
recordLastKnownStructure(map, this)
|
||||
last = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
this@LinkedHashMap.clear()
|
||||
}
|
||||
|
||||
override operator fun contains(o: Any?): Boolean {
|
||||
if (o is Entry<*, *>) {
|
||||
return containsEntry(o as Entry<*, *>?)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override operator fun iterator(): Iterator<Entry<K, V>> {
|
||||
return EntryIterator()
|
||||
}
|
||||
|
||||
override fun remove(entry: Any?): Boolean {
|
||||
if (contains(entry)) {
|
||||
val key = (entry as Entry<*, *>).key
|
||||
this@LinkedHashMap.remove(key)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return this@LinkedHashMap.size
|
||||
}
|
||||
}
|
||||
|
||||
// True if we should use the access order (ie, for LRU caches) instead of
|
||||
// insertion order.
|
||||
@Transient private val accessOrder: Boolean
|
||||
|
||||
/*
|
||||
* The head of the LRU/insert order chain, which is a doubly-linked circular
|
||||
* list. The key and value of head should never be read.
|
||||
*
|
||||
* The most recently inserted/accessed node is at the end of the chain, ie.
|
||||
* chain.prev.
|
||||
*/
|
||||
@Transient private val head = ChainEntry()
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@Transient private val map = HashMap<K, ChainEntry>()
|
||||
|
||||
constructor() {
|
||||
resetChainEntries()
|
||||
}
|
||||
|
||||
@JvmOverloads constructor(ignored: Int, alsoIgnored: Float = 0f) : super(ignored, alsoIgnored) {
|
||||
resetChainEntries()
|
||||
}
|
||||
|
||||
constructor(ignored: Int, alsoIgnored: Float, accessOrder: Boolean) : super(ignored, alsoIgnored) {
|
||||
this.accessOrder = accessOrder
|
||||
resetChainEntries()
|
||||
}
|
||||
|
||||
constructor(toBeCopied: Map<out K, V>) {
|
||||
resetChainEntries()
|
||||
this.putAll(toBeCopied)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
map.clear()
|
||||
resetChainEntries()
|
||||
}
|
||||
|
||||
private fun resetChainEntries() {
|
||||
head.prev = head
|
||||
head.next = head
|
||||
}
|
||||
|
||||
override fun clone(): Any {
|
||||
return LinkedHashMap(this)
|
||||
}
|
||||
|
||||
override fun containsKey(key: Any?): Boolean {
|
||||
return map.containsKey(key)
|
||||
}
|
||||
|
||||
override fun containsValue(value: Any?): Boolean {
|
||||
var node: ChainEntry = head.next
|
||||
while (node !== head) {
|
||||
if (node.value == value) {
|
||||
return true
|
||||
}
|
||||
node = node.next
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun entrySet(): Set<Entry<K, V>> {
|
||||
return EntrySet()
|
||||
}
|
||||
|
||||
override operator fun get(key: Any?): V? {
|
||||
val entry = map.get(key)
|
||||
if (entry != null) {
|
||||
recordAccess(entry)
|
||||
return entry!!.value
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun put(key: K?, value: V?): V? {
|
||||
val old = map.get(key)
|
||||
if (old == null) {
|
||||
val newEntry = ChainEntry(key, value)
|
||||
map.put(key, newEntry)
|
||||
newEntry.addToEnd()
|
||||
val eldest = head.next
|
||||
if (removeEldestEntry(eldest)) {
|
||||
eldest!!.remove()
|
||||
map.remove(eldest.key)
|
||||
}
|
||||
return null
|
||||
}
|
||||
else {
|
||||
val oldValue = old!!.setValue(value)
|
||||
recordAccess(old)
|
||||
return oldValue
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(key: Any?): V? {
|
||||
val entry = map.remove(key)
|
||||
if (entry != null) {
|
||||
entry!!.remove()
|
||||
return entry!!.value
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return map.size
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected fun removeEldestEntry(eldest: Entry<K, V>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun recordAccess(entry: ChainEntry) {
|
||||
if (accessOrder) {
|
||||
// Move to the tail of the chain on access.
|
||||
entry.remove()
|
||||
entry.addToEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Based on GWT LinkedHashSet
|
||||
* Copyright 2008 Google Inc.
|
||||
*/
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
open class LinkedHashSet<E> : HashSet<E> {
|
||||
|
||||
constructor() : super(LinkedHashMap<E, Any>())
|
||||
|
||||
constructor(c: Collection<E>) : super(LinkedHashMap<E, Any>()) {
|
||||
addAll(c)
|
||||
}
|
||||
|
||||
constructor(capacity: Int) : super(LinkedHashMap<E, Any>(capacity))
|
||||
constructor(capacity: Int, loadFactor: Float = 0.0f) : super(LinkedHashMap<E, Any>(capacity, loadFactor))
|
||||
|
||||
// public override fun clone(): Any {
|
||||
// return LinkedHashSet(this)
|
||||
// }
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user