Maps and sets: cleanup, simplify, refactor.

#KT-12386
This commit is contained in:
Ilya Gorbunov
2016-08-26 03:24:55 +03:00
parent b05c2c1f6a
commit 5cd3417d4c
9 changed files with 317 additions and 383 deletions
@@ -20,42 +20,33 @@
package kotlin.collections package kotlin.collections
import kotlin.collections.Map.Entry
import kotlin.collections.MutableMap.MutableEntry
abstract class AbstractHashMap<K, V> : AbstractMap<K, V> { abstract class AbstractHashMap<K, V> : AbstractMap<K, V> {
private inner class EntrySet : AbstractSet<Entry<K, V>>() { private inner class EntrySet : AbstractSet<MutableEntry<K, V>>() {
override fun clear() { override fun clear() {
this@AbstractHashMap.clear() this@AbstractHashMap.clear()
} }
override operator fun contains(o: Any?): Boolean { override operator fun contains(element: MutableEntry<K, V>): Boolean = containsEntry(element)
if (o is Entry<*, *>) {
return containsEntry(o as Entry<*, *>?)
}
return false
}
override operator fun iterator(): Iterator<Entry<K, V>> { override operator fun iterator(): MutableIterator<MutableEntry<K, V>> = hashCodeMap.iterator()
return EntrySetIterator()
}
override fun remove(entry: Any?): Boolean { override fun remove(entry: MutableEntry<K, V>): Boolean {
if (contains(entry)) { if (contains(entry)) {
val key = (entry as Entry<*, *>).key this@AbstractHashMap.remove(entry.key)
this@AbstractHashMap.remove(key)
return true return true
} }
return false return false
} }
override fun size(): Int { override val size: Int get() = this@AbstractHashMap.size
return this@AbstractHashMap.size
}
} }
/** /*
* Iterator for `EntrySet`.
*/
private inner class EntrySetIterator : Iterator<Entry<K, V>> { private inner class EntrySetIterator : Iterator<Entry<K, V>> {
private val stringMapEntries = stringMap!!.iterator() private val stringMapEntries = stringMap!!.iterator()
private var current: MutableIterator<Entry<K, V>> = stringMapEntries private var current: MutableIterator<Entry<K, V>> = stringMapEntries
@@ -102,109 +93,103 @@ abstract class AbstractHashMap<K, V> : AbstractMap<K, V> {
recordLastKnownStructure(this@AbstractHashMap, this) recordLastKnownStructure(this@AbstractHashMap, this)
} }
} }*/
/** /**
* A map of integral hashCodes onto entries. * A map of integral hashCodes onto entries.
*/ */
@Transient private var hashCodeMap: InternalHashCodeMap<K, V>? = null private var hashCodeMap = InternalHashCodeMap<K, V>(this)
/** // /**
* A map of Strings onto values. // * A map of Strings onto values.
*/ // */
@Transient private var stringMap: InternalStringMap<K, V>? = null // @Transient private var stringMap: InternalStringMap<K, V>? = null
constructor() { constructor() : super()
reset() // init {
} // reset()
// }
@JvmOverloads constructor(ignored: Int, alsoIgnored: Float = 0f) { constructor(capacity: Int, loadFactor: Float = 0f) : this() {
// This implementation of HashMap has no need of load factors or capacities. // This implementation of HashMap has no need of load factors or capacities.
checkArgument(ignored >= 0, "Negative initial capacity") require(capacity >= 0) { "Negative initial capacity" }
checkArgument(alsoIgnored >= 0, "Non-positive load factor") require(loadFactor >= 0) { "Non-positive load factor" }
reset()
} }
constructor(toBeCopied: Map<out K, V>) { constructor(original: Map<out K, V>) : this() {
reset() this.putAll(original)
this.putAll(toBeCopied)
} }
override fun clear() { override fun clear() {
reset() // reset()
} // }
//
private fun reset() { // private fun reset() {
hashCodeMap = InternalHashCodeMap<K, V>(this) hashCodeMap = InternalHashCodeMap<K, V>(this)
stringMap = InternalStringMap<K, V>(this) // stringMap = InternalStringMap<K, V>(this)
structureChanged(this) // structureChanged(this)
} }
@SpecializeMethod(params = { String.class }, target = "hasStringValue") // @SpecializeMethod(params = { String.class }, target = "hasStringValue")
override fun containsKey(key: Any?): Boolean { override fun containsKey(key: K): Boolean {
return if (key is String) return hasHashValue(key)
hasStringValue(JsUtils.unsafeCastToString(key)) // return if (key is String)
else // hasStringValue(JsUtils.unsafeCastToString(key))
hasHashValue(key) // else
// hasHashValue(key)
} }
override fun containsValue(value: Any?): Boolean { override fun containsValue(value: V): Boolean {
return containsValue(value, stringMap) || containsValue(value, hashCodeMap) return /*containsValue(value, stringMap) || */ containsValue(value, hashCodeMap)
} }
private fun containsValue(value: Any, entries: Iterable<Entry<K, V>>): Boolean { private fun containsValue(value: V, entries: Iterable<Entry<K, V>>): Boolean = entries.any { equals(it.value, value) }
for (entry in entries) {
if (equals(value, entry.value)) { override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
return true get() = EntrySet()
}
} // @SpecializeMethod(params = { String.class }, target = "getStringValue")
return false override operator fun get(key: K): V? {
return getHashValue(key)
// return if (key is String)
// getStringValue(JsUtils.unsafeCastToString(key))
// else
// getHashValue(key)
} }
override fun entrySet(): Set<Entry<K, V>> { // @SpecializeMethod(params = { String.class, Object .class }, target = "putStringValue")
return EntrySet() override fun put(key: K, value: V): V? {
return putHashValue(key, value)
// return if (key is String)
// putStringValue(JsUtils.unsafeCastToString(key), value)
// else
// putHashValue(key, value)
} }
@SpecializeMethod(params = { String.class }, target = "getStringValue") // @SpecializeMethod(params = { String.class }, target = "removeStringValue")
override operator fun get(key: Any?): V { override fun remove(key: K): V? {
return if (key is String) return removeHashValue(key)
getStringValue(JsUtils.unsafeCastToString(key)) // return if (key is String)
else // removeStringValue(JsUtils.unsafeCastToString(key))
getHashValue(key) // else
// removeHashValue(key)
} }
@SpecializeMethod(params = { String.class, Object .class }, target = "putStringValue") override val size: Int get() {
override fun put(key: K?, value: V?): V { return hashCodeMap.size /*+ stringMap!!.size()*/
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 * Subclasses must override to return a whether or not two keys or values are
* equal. * equal.
*/ */
internal abstract fun equals(value1: Any, value2: Any): Boolean internal abstract fun equals(value1: Any?, value2: Any?): Boolean
/** /**
* Subclasses must override to return a hash code for a given key. The key is * Subclasses must override to return a hash code for a given key. The key is
* guaranteed to be non-null and not a String. * guaranteed to be non-null and not a String.
*/ */
internal abstract fun getHashCode(key: Any): Int internal abstract fun getHashCode(key: K): Int
/** /**
* Returns the Map.Entry whose key is Object equal to `key`, * Returns the Map.Entry whose key is Object equal to `key`,
@@ -212,51 +197,51 @@ abstract class AbstractHashMap<K, V> : AbstractMap<K, V> {
* or `null` if no such Map.Entry exists at the specified * or `null` if no such Map.Entry exists at the specified
* hashCode. * hashCode.
*/ */
private fun getHashValue(key: Any?): V { private fun getHashValue(key: K): V? {
return getEntryValueOrNull(hashCodeMap!!.getEntry(key)) return hashCodeMap.getEntry(key)?.value
} }
/** // /**
* Returns the value for the given key in the stringMap. Returns // * Returns the value for the given key in the stringMap. Returns
* `null` if the specified key does not exist. // * `null` if the specified key does not exist.
*/ // */
private fun getStringValue(key: String?): V { // private fun getStringValue(key: String?): V {
return if (key == null) getHashValue(null) else stringMap!!.get(key) // 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 * Returns true if the a key exists in the hashCodeMap that is Object equal to
* `key`, provided that `key`'s hash code is * `key`, provided that `key`'s hash code is
* `hashCode`. * `hashCode`.
*/ */
private fun hasHashValue(key: Any?): Boolean { private fun hasHashValue(key: K): Boolean {
return hashCodeMap!!.getEntry(key) != null 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)
} }
//
// /**
// * 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 * Sets the specified key to the specified value in the hashCodeMap. Returns
* the value previously at that key. Returns `null` if the * the value previously at that key. Returns `null` if the
* specified key did not exist. * specified key did not exist.
*/ */
private fun putHashValue(key: K?, value: V): V { private fun putHashValue(key: K, value: V): V? {
return hashCodeMap!!.put(key, value) return hashCodeMap.put(key, value)
} }
/** // /**
* Sets the specified key to the specified value in the stringMap. Returns the // * Sets the specified key to the specified value in the stringMap. Returns the
* value previously at that key. Returns `null` if the specified // * value previously at that key. Returns `null` if the specified
* key did not exist. // * key did not exist.
*/ // */
private fun putStringValue(key: String?, value: V): V { // private fun putStringValue(key: String?, value: V): V {
return if (key == null) putHashValue(null, value) else stringMap!!.put(key, value) // return if (key == null) putHashValue(null, value) else stringMap!!.put(key, value)
} // }
/** /**
* Removes the pair whose key is Object equal to `key` from * Removes the pair whose key is Object equal to `key` from
@@ -264,16 +249,16 @@ abstract class AbstractHashMap<K, V> : AbstractMap<K, V> {
* is `hashCode`. Returns the value that was associated with the * is `hashCode`. Returns the value that was associated with the
* removed key, or null if no such key existed. * removed key, or null if no such key existed.
*/ */
private fun removeHashValue(key: Any?): V { private fun removeHashValue(key: K): V? {
return hashCodeMap!!.remove(key) return hashCodeMap.remove(key)
} }
/** // /**
* Removes the specified key from the stringMap and returns the value that was // * Removes the specified key from the stringMap and returns the value that was
* previously there. Returns `null` if the specified key does not // * previously there. Returns `null` if the specified key does not
* exist. // * exist.
*/ // */
private fun removeStringValue(key: String?): V { // private fun removeStringValue(key: String?): V {
return if (key == null) removeHashValue(null) else stringMap!!.remove(key) // return if (key == null) removeHashValue(null) else stringMap!!.remove(key)
} // }
}// This implementation of HashMap has no need of initial capacities. }
@@ -66,7 +66,9 @@ abstract class AbstractMap<K, V> protected constructor() : MutableMap<K, V> {
override fun containsValue(value: V): Boolean = entries.any { it.value == value } override fun containsValue(value: V): Boolean = entries.any { it.value == value }
internal fun containsEntry(entry: Map.Entry<*, *>): Boolean { internal fun containsEntry(entry: Map.Entry<*, *>?): Boolean {
// since entry comes from @UnsafeVariance parameters it can be virtually anything
if (entry !is Map.Entry<*, *>) return false
val key = entry.key val key = entry.key
val value = entry.value val value = entry.value
val ourValue = get(key) val ourValue = get(key)
@@ -20,45 +20,18 @@
package kotlin.collections package kotlin.collections
open class HashMap<K, V> : AbstractHashMap<K, V>, Cloneable, Serializable {
/** open class HashMap<K, V> : AbstractHashMap<K, V> {
* 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
/** constructor() : super()
* Ensures that RPC will consider type parameter V to be exposed. It will be constructor(capacity: Int, loadFactor: Float = 0f) : super(capacity, loadFactor)
* pruned by dead code elimination. constructor(original: Map<out K, V>) : super(original)
*/
@SuppressWarnings("unused")
private val exposeValue: V? = null
constructor() { // public override fun clone(): Any {
} // return HashMap<K, V>(this)
// }
constructor(ignored: Int) : super(ignored) { override fun equals(value1: Any?, value2: Any?): Boolean = value1 == value2
}
constructor(ignored: Int, alsoIgnored: Float) : super(ignored, alsoIgnored) { override fun getHashCode(key: K): Int = key?.hashCode() ?: 0
}
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)
}
} }
+14 -41
View File
@@ -21,22 +21,9 @@
package kotlin.collections package kotlin.collections
/** open class HashSet<E> : AbstractSet<E> {
* 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. private val map: HashMap<E, Any>
*/
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() { constructor() {
map = HashMap<E, Any>() map = HashMap<E, Any>()
@@ -47,11 +34,7 @@ open class HashSet<E> : AbstractSet<E>, Set<E>, Cloneable, Serializable {
addAll(c) addAll(c)
} }
constructor(initialCapacity: Int) { constructor(initialCapacity: Int, loadFactor: Float = 0.0f) {
map = HashMap<E, Any>(initialCapacity)
}
constructor(initialCapacity: Int, loadFactor: Float) {
map = HashMap<E, Any>(initialCapacity, loadFactor) map = HashMap<E, Any>(initialCapacity, loadFactor)
} }
@@ -65,37 +48,27 @@ open class HashSet<E> : AbstractSet<E>, Set<E>, Cloneable, Serializable {
this.map = map this.map = map
} }
override fun add(o: E?): Boolean { override fun add(element: E): Boolean {
val old = map!!.put(o, this) val old = map.put(element, this)
return old == null return old == null
} }
override fun clear() { override fun clear() {
map!!.clear() map.clear()
} }
public override fun clone(): Any { // public override fun clone(): Any {
return HashSet<E>(this) // return HashSet<E>(this)
} // }
override operator fun contains(o: Any?): Boolean { override operator fun contains(element: E): Boolean = map.containsKey(element)
return map!!.containsKey(o)
}
override fun isEmpty(): Boolean { override fun isEmpty(): Boolean = map.isEmpty()
return map!!.isEmpty()
}
override fun iterator(): Iterator<E> { override fun iterator(): MutableIterator<E> = map.keys.iterator()
return map!!.keys.iterator()
}
override fun remove(o: Any?): Boolean { override fun remove(element: E): Boolean = map.remove(element) != null
return map!!.remove(o) != null
}
override fun size(): Int { override val size: Int get() = map.size
return map!!.size
}
} }
@@ -20,11 +20,8 @@
package kotlin.collections package kotlin.collections
import java.util.ConcurrentModificationDetector.structureChanged import kotlin.collections.MutableMap.MutableEntry
import kotlin.collections.AbstractMap.SimpleEntry
import java.util.AbstractMap.SimpleEntry
import javaemul.internal.ArrayHelper
/** /**
* A simple wrapper around JavaScriptObject to provide [java.util.Map]-like semantics for any * A simple wrapper around JavaScriptObject to provide [java.util.Map]-like semantics for any
@@ -38,126 +35,117 @@ import javaemul.internal.ArrayHelper
* have the same hash, each value in hashCodeMap is actually an array containing all entries whose * have the same hash, each value in hashCodeMap is actually an array containing all entries whose
* keys share the same hash. * keys share the same hash.
*/ */
private class InternalHashCodeMap<K, V>(private val host: AbstractHashMap<K, V>) : Iterable<Entry<K, V>> { internal class InternalHashCodeMap<K, V>(private val host: AbstractHashMap<K, V>) : MutableIterable<MutableEntry<K, V>> {
private val backingMap = InternalJsMapFactory.newJsMap() private val backingMap: dynamic = js("new Object()")
private var size: Int = 0 var size: Int = 0
private set
fun put(key: K, value: V): V? { fun put(key: K, value: V): V? {
val hashCode = hash(key) val hashCode = host.getHashCode(key)
val chain = getChainOrEmpty(hashCode) val chain = getChainOrNull(hashCode)
if (chain == null) {
if (chain.size == 0) {
// This is a new chain, put it to the map. // This is a new chain, put it to the map.
backingMap.set(hashCode, chain) backingMap[hashCode] = arrayOf(SimpleEntry(key, value))
} }
else { else {
// Chain already exists, perhaps key also exists. // Chain already exists, perhaps key also exists.
val entry = findEntryInChain(key, chain) val entry = chain.findEntryInChain(key)
if (entry != null) { if (entry != null) {
return entry!!.setValue(value) return entry.setValue(value)
} }
chain.asDynamic().push(SimpleEntry(key, value))
} }
chain[chain.size] = SimpleEntry<K, V>(key, value)
size++ size++
structureChanged(host) // structureChanged(host)
return null return null
} }
fun remove(key: Any): V? { fun remove(key: K): V? {
val hashCode = hash(key) val hashCode = host.getHashCode(key)
val chain = getChainOrEmpty(hashCode) val chain = getChainOrNull(hashCode) ?: return null
for (i in chain.indices) { for (index in 0..chain.size-1) {
val entry = chain[i] val entry = chain[index]
if (host.equals(key, entry.key)) { if (host.equals(key, entry.key)) {
if (chain.size == 1) { if (chain.size == 1) {
ArrayHelper.setLength(chain, 0) chain.asDynamic().length = 0
// remove the whole array // remove the whole array
backingMap.delete(hashCode) deleteProperty(backingMap, hashCode)
} }
else { else {
// splice out the entry we're removing // splice out the entry we're removing
ArrayHelper.removeFrom(chain, i, 1) chain.asDynamic().splice(index, 1)
} }
size-- size--
structureChanged(host) // structureChanged(host)
return entry.value return entry.value
} }
} }
return null return null
} }
fun getEntry(key: Any): Entry<K, V> { fun getEntry(key: K): MutableEntry<K, V>? =
return findEntryInChain(key, getChainOrEmpty(hash(key))) getChainOrNull(host.getHashCode(key))?.findEntryInChain(key)
}
private fun findEntryInChain(key: Any, chain: Array<Entry<K, V>>): Entry<K, V>? { private fun Array<MutableEntry<K, V>>.findEntryInChain(key: K): MutableEntry<K, V>? =
for (entry in chain) { firstOrNull { entry -> host.equals(entry.key, key) }
if (host.equals(key, entry.key)) {
return entry override fun iterator(): MutableIterator<MutableEntry<K, V>> {
return object : MutableIterator<MutableEntry<K, V>> {
var state = -1 // -1 not ready, 0 - ready, 1 - done
val keys: Array<Int> = js("Object").keys(backingMap)
var keyIndex = -1
var chain: Array<MutableEntry<K, V>>? = null
var itemIndex = -1
var lastEntry: MutableEntry<K, V>? = null
private fun computeNext(): Int {
if (chain != null) {
if (++itemIndex < chain!!.size)
return 0
}
if (++keyIndex < keys.size) {
chain = backingMap[keys[keyIndex]]
itemIndex = 0
return 0
}
else {
chain = null
return 1
}
} }
}
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 { override fun hasNext(): Boolean {
if (itemIndex < chain.size) { if (state == -1)
return true state = computeNext()
} return state == 0
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> { override fun next(): MutableEntry<K, V> {
lastEntry = chain[itemIndex++] if (!hasNext()) throw NoSuchElementException()
val lastEntry = chain!![itemIndex]
this.lastEntry = lastEntry
state = -1
return lastEntry return lastEntry
} }
override fun remove() { override fun remove() {
checkNotNull(lastEntry)
this@InternalHashCodeMap.remove(lastEntry!!.key) this@InternalHashCodeMap.remove(lastEntry!!.key)
// Unless we are in a new chain, all items have shifted so our itemIndex should as well... lastEntry = null
if (itemIndex != 0) { // the chain being iterated just got modified by InternalHashCodeMap.remove
itemIndex-- itemIndex--
}
} }
} }
} }
private fun getChainOrEmpty(hashCode: Int): Array<Entry<K, V>> { private fun getChainOrNull(hashCode: Int): Array<MutableEntry<K, V>>? {
val chain = unsafeCastToArray(backingMap.get(hashCode)) val chain = backingMap[hashCode]
return chain ?: newEntryChain() return if (chain !== undefined) chain else null // satisfying { it != undefined }
} }
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)
}
} }
@@ -19,6 +19,8 @@
*/ */
package kotlin.collections package kotlin.collections
import kotlin.collections.MutableMap.MutableEntry
open class LinkedHashMap<K, V> : HashMap<K, V>, Map<K, V> { open class LinkedHashMap<K, V> : HashMap<K, V>, Map<K, V> {
/** /**
@@ -33,41 +35,59 @@ open class LinkedHashMap<K, V> : HashMap<K, V>, Map<K, V> {
* small modifications. Paying a small storage cost only if you use * small modifications. Paying a small storage cost only if you use
* LinkedHashMap and minimizing code size seemed like a better tradeoff * 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) { private inner class ChainEntry(key: K, value: V) : AbstractMap.SimpleEntry<K, V>(key, value) {
@Transient private var next: ChainEntry? = null internal var next: ChainEntry? = null
@Transient private var prev: ChainEntry? = null internal var prev: ChainEntry? = null
/** /**
* Add this node to the end of the chain. * Add this node to the end of the chain.
*/ */
fun addToEnd() { fun addToEnd() {
val tail = head.prev
// Chain is valid.
assert(head != null && tail != null)
// This entry is not in the list. // This entry is not in the list.
assert(next == null && prev == null) check(next == null && prev == null)
if (head == null) {
head = this
next = this
prev = this
} else {
// Chain is valid.
val tail = checkNotNull(head).prev
checkNotNull(tail)
// Update me.
prev = tail
next = head
// Update my new siblings: current head and old tail
head!!.prev = this
tail!!.next = this
}
// Update me.
prev = tail
next = head
tail!!.next = head.prev = this
} }
/** /**
* Remove this node from any list it may be a part of. * Remove this node from any list it may be a part of.
*/ */
fun remove() { fun remove() {
next!!.prev = prev if (this.next === this) {
prev!!.next = next // if this is single element, remove head
next = prev = null 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
} }
} }
private inner class EntrySet : AbstractSet<Entry<K, V>>() { private inner class EntrySet : AbstractSet<MutableEntry<K, V>>() {
private inner class EntryIterator : Iterator<Entry<K, V>> { private inner class EntryIterator : MutableIterator<MutableEntry<K, V>> {
// The last entry that was returned from this iterator. // The last entry that was returned from this iterator.
private var last: ChainEntry? = null private var last: ChainEntry? = null
@@ -75,30 +95,32 @@ open class LinkedHashMap<K, V> : HashMap<K, V>, Map<K, V> {
private var next: ChainEntry? = null private var next: ChainEntry? = null
init { init {
next = head.next next = head
recordLastKnownStructure(map, this) // recordLastKnownStructure(map, this)
} }
override fun hasNext(): Boolean { override fun hasNext(): Boolean {
return next !== head return next !== null
} }
override fun next(): Entry<K, V> { override fun next(): MutableEntry<K, V> {
checkStructuralChange(map, this) // checkStructuralChange(map, this)
checkCriticalElement(hasNext()) if (!hasNext()) throw NoSuchElementException()
last = next val current = next!!
next = next!!.next last = current
return last next = current.next
if (next === head) next = null // satisfying { it != head }
return current
} }
override fun remove() { override fun remove() {
checkState(last != null) check(last != null)
checkStructuralChange(map, this) // checkStructuralChange(map, this)
last!!.remove() last!!.remove()
map.remove(last!!.key) map.remove(last!!.key)
recordLastKnownStructure(map, this) // recordLastKnownStructure(map, this)
last = null last = null
} }
} }
@@ -107,34 +129,24 @@ open class LinkedHashMap<K, V> : HashMap<K, V>, Map<K, V> {
this@LinkedHashMap.clear() this@LinkedHashMap.clear()
} }
override operator fun contains(o: Any?): Boolean { override operator fun contains(element: MutableEntry<K, V>): Boolean = containsEntry(element)
if (o is Entry<*, *>) {
return containsEntry(o as Entry<*, *>?)
}
return false
}
override operator fun iterator(): Iterator<Entry<K, V>> { override operator fun iterator(): MutableIterator<MutableEntry<K, V>> = EntryIterator()
return EntryIterator()
}
override fun remove(entry: Any?): Boolean { override fun remove(entry: MutableEntry<K, V>): Boolean {
if (contains(entry)) { if (contains(entry)) {
val key = (entry as Entry<*, *>).key this@LinkedHashMap.remove(entry.key)
this@LinkedHashMap.remove(key)
return true return true
} }
return false return false
} }
override fun size(): Int { override val size: Int get() = this@LinkedHashMap.size
return this@LinkedHashMap.size
}
} }
// True if we should use the access order (ie, for LRU caches) instead of // // True if we should use the access order (ie, for LRU caches) instead of
// insertion order. // // insertion order.
@Transient private val accessOrder: Boolean // private val accessOrder: Boolean
/* /*
* The head of the LRU/insert order chain, which is a doubly-linked circular * The head of the LRU/insert order chain, which is a doubly-linked circular
@@ -143,31 +155,31 @@ open class LinkedHashMap<K, V> : HashMap<K, V>, Map<K, V> {
* The most recently inserted/accessed node is at the end of the chain, ie. * The most recently inserted/accessed node is at the end of the chain, ie.
* chain.prev. * chain.prev.
*/ */
@Transient private val head = ChainEntry() private var head: ChainEntry? = null
/* /*
* The hashmap that keeps track of our entries and the chain. Note that we * 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 * duplicate the key here to eliminate changes to HashMap and minimize the
* code here, at the expense of additional space. * code here, at the expense of additional space.
*/ */
@Transient private val map = HashMap<K, ChainEntry>() private val map = HashMap<K, ChainEntry>()
constructor() { constructor() {
resetChainEntries() resetChainEntries()
} }
@JvmOverloads constructor(ignored: Int, alsoIgnored: Float = 0f) : super(ignored, alsoIgnored) { constructor(ignored: Int, alsoIgnored: Float = 0f) : super(ignored, alsoIgnored) {
resetChainEntries() resetChainEntries()
} }
constructor(ignored: Int, alsoIgnored: Float, accessOrder: Boolean) : super(ignored, alsoIgnored) { // constructor(ignored: Int, alsoIgnored: Float, accessOrder: Boolean) : super(ignored, alsoIgnored) {
this.accessOrder = accessOrder // this.accessOrder = accessOrder
resetChainEntries() // resetChainEntries()
} // }
constructor(toBeCopied: Map<out K, V>) { constructor(original: Map<out K, V>) {
resetChainEntries() resetChainEntries()
this.putAll(toBeCopied) this.putAll(original)
} }
override fun clear() { override fun clear() {
@@ -176,85 +188,80 @@ open class LinkedHashMap<K, V> : HashMap<K, V>, Map<K, V> {
} }
private fun resetChainEntries() { private fun resetChainEntries() {
head.prev = head head = null
head.next = head
} }
//
// override fun clone(): Any {
// return LinkedHashMap(this)
// }
override fun clone(): Any { override fun containsKey(key: K): Boolean = map.containsKey(key)
return LinkedHashMap(this)
}
override fun containsKey(key: Any?): Boolean { override fun containsValue(value: V): Boolean {
return map.containsKey(key) var node: ChainEntry = head ?: return false
} do {
override fun containsValue(value: Any?): Boolean {
var node: ChainEntry = head.next
while (node !== head) {
if (node.value == value) { if (node.value == value) {
return true return true
} }
node = node.next node = node.next!!
} } while (node !== head)
return false return false
} }
override fun entrySet(): Set<Entry<K, V>> {
return EntrySet()
}
override operator fun get(key: Any?): V? { override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() = EntrySet()
override operator fun get(key: K): V? {
val entry = map.get(key) val entry = map.get(key)
if (entry != null) { if (entry != null) {
recordAccess(entry) recordAccess(entry)
return entry!!.value return entry.value
} }
return null return null
} }
override fun put(key: K?, value: V?): V? { override fun put(key: K, value: V): V? {
val old = map.get(key) val old = map.get(key)
if (old == null) { if (old == null) {
val newEntry = ChainEntry(key, value) val newEntry = ChainEntry(key, value)
map.put(key, newEntry) map.put(key, newEntry)
newEntry.addToEnd() newEntry.addToEnd()
val eldest = head.next // val eldest = head.next!!
if (removeEldestEntry(eldest)) { // if (removeEldestEntry(eldest)) {
eldest!!.remove() // eldest.remove()
map.remove(eldest.key) // map.remove(eldest.key)
} // }
return null return null
} }
else { else {
val oldValue = old!!.setValue(value) val oldValue = old.setValue(value)
recordAccess(old) recordAccess(old)
return oldValue return oldValue
} }
} }
override fun remove(key: Any?): V? { override fun remove(key: K): V? {
val entry = map.remove(key) val entry = map.remove(key)
if (entry != null) { if (entry != null) {
entry!!.remove() entry.remove()
return entry!!.value return entry.value
} }
return null return null
} }
override fun size(): Int { override val size: Int get() = map.size
return map.size
}
@SuppressWarnings("unused") // @SuppressWarnings("unused")
protected fun removeEldestEntry(eldest: Entry<K, V>): Boolean { // protected fun removeEldestEntry(eldest: Entry<K, V>): Boolean {
return false // return false
} // }
private fun recordAccess(entry: ChainEntry) { private fun recordAccess(entry: ChainEntry) {
if (accessOrder) { // if (accessOrder) {
// Move to the tail of the chain on access. // // Move to the tail of the chain on access.
entry.remove() // entry.remove()
entry.addToEnd() // entry.addToEnd()
} // }
} }
} }
@@ -28,7 +28,6 @@ open class LinkedHashSet<E> : HashSet<E> {
addAll(c) addAll(c)
} }
constructor(capacity: Int) : super(LinkedHashMap<E, Any>(capacity))
constructor(capacity: Int, loadFactor: Float = 0.0f) : super(LinkedHashMap<E, Any>(capacity, loadFactor)) constructor(capacity: Int, loadFactor: Float = 0.0f) : super(LinkedHashMap<E, Any>(capacity, loadFactor))
// public override fun clone(): Any { // public override fun clone(): Any {
+3
View File
@@ -40,3 +40,6 @@ public fun js(code: String): dynamic = noImpl
* Function corresponding to JavaScript's `typeof` operator * Function corresponding to JavaScript's `typeof` operator
*/ */
public inline fun jsTypeOf(a: Any?): String = js("typeof a") public inline fun jsTypeOf(a: Any?): String = js("typeof a")
@library
internal fun deleteProperty(`object`: Any, property: Any): Unit = noImpl
+4
View File
@@ -902,6 +902,10 @@
return new Kotlin.ArrayIterator(array); return new Kotlin.ArrayIterator(array);
}; };
Kotlin.deleteProperty = function (object, property) {
delete object[property];
};
Kotlin.jsonAddProperties = function (obj1, obj2) { Kotlin.jsonAddProperties = function (obj1, obj2) {
for (var p in obj2) { for (var p in obj2) {
if (obj2.hasOwnProperty(p)) { if (obj2.hasOwnProperty(p)) {