diff --git a/js/js.libraries/src/core/collections/AbstractList.kt b/js/js.libraries/src/core/collections/AbstractList.kt index df82a0dfe95..cd391306b55 100644 --- a/js/js.libraries/src/core/collections/AbstractList.kt +++ b/js/js.libraries/src/core/collections/AbstractList.kt @@ -93,27 +93,11 @@ public abstract class AbstractList protected constructor() : AbstractCollecti override fun equals(other: Any?): Boolean { if (other === this) return true if (other !is List<*>) return false - if (size != other.size) return false - val otherIterator = other.iterator() - for (elem in this) { - val elemOther = otherIterator.next() - if (elem != elemOther) { - return false - } - } - - return true + return orderedEquals(this, other) } - override fun hashCode(): Int { - var hashCode = 1 - for (e in this) { - hashCode = 31 * hashCode + (e?.hashCode() ?: 0) - hashCode = hashCode or 0 // make sure we don't overflow - } - return hashCode - } + override fun hashCode(): Int = orderedHashCode(this) private open inner class IteratorImpl : MutableIterator { @@ -236,6 +220,28 @@ public abstract class AbstractList protected constructor() : AbstractCollecti throw IllegalArgumentException("fromIndex: $start > toIndex: $end") } } + + internal fun orderedHashCode(c: Collection<*>): Int { + var hashCode = 1 + for (e in c) { + hashCode = 31 * hashCode + (e?.hashCode() ?: 0) + hashCode = hashCode or 0 // make sure we don't overflow + } + return hashCode + } + + internal fun orderedEquals(c: Collection<*>, other: Collection<*>): Boolean { + if (c.size != other.size) return false + + val otherIterator = other.iterator() + for (elem in c) { + val elemOther = otherIterator.next() + if (elem != elemOther) { + return false + } + } + return true + } } } diff --git a/js/js.libraries/src/core/collections/AbstractMap.kt b/js/js.libraries/src/core/collections/AbstractMap.kt index 8b83dc37492..62e4fc39c56 100644 --- a/js/js.libraries/src/core/collections/AbstractMap.kt +++ b/js/js.libraries/src/core/collections/AbstractMap.kt @@ -25,88 +25,48 @@ abstract class AbstractMap protected constructor() : MutableMap { /** * A mutable [Map.Entry] shared by several [Map] implementations. */ - open class SimpleEntry : AbstractEntry { - constructor(key: K, value: V) : super(key, value) { + open class SimpleEntry(override val key: K, value: V) : MutableMap.MutableEntry { + constructor(entry: Map.Entry) : this(entry.key, entry.value) + + private var _value = value + + override val value: V get() = _value + + override fun setValue(value: V): V { + val oldValue = this._value + this._value = value + return oldValue } - constructor(entry: Entry) : super(entry.key, entry.value) { - } + override fun hashCode(): Int = entryHashCode(this) + override fun toString(): String = entryToString(this) + override fun equals(other: Any?): Boolean = entryEquals(this, other) + } /** * An immutable [Map.Entry] shared by several [Map] implementations. */ - class SimpleImmutableEntry : AbstractEntry { - constructor(key: K, value: V) : super(key, value) { - } + class SimpleImmutableEntry(override val key: K, override val value: V) : Map.Entry { + constructor(entry: Map.Entry) : this(entry.key, entry.value) - constructor(entry: Entry) : super(entry.key, entry.value) { - } - - override fun setValue(value: V): V { - throw UnsupportedOperationException() - } + override fun hashCode(): Int = entryHashCode(this) + override fun toString(): String = entryToString(this) + override fun equals(other: Any?): Boolean = entryEquals(this, other) } - /** - * Basic [Map.Entry] implementation used by [SimpleEntry] - * and [SimpleImmutableEntry]. - */ - private abstract class AbstractEntry protected constructor(private val key: K, private var value: V?) : Map.Entry { - - override fun getKey(): K { - return key - } - - override fun getValue(): V { - return value - } - - override fun setValue(value: V): V { - val oldValue = this.value - this.value = value - return oldValue - } - - override fun equals(other: Any?): Boolean { - if (other !is Entry<*, *>) { - return false - } - val entry = other as Entry<*, *>? - return key == entry!!.key && value == entry!!.value - } - - /** - * Calculate the hash code using Sun's specified algorithm. - */ - override fun hashCode(): Int { - return Objects.hashCode(key) xor Objects.hashCode(value) - } - - override fun toString(): String { - // for compatibility with the real Jre: issue 3422 - return key + "=" + value - } - } override fun clear() { entries.clear() } - override fun containsKey(key: Any): Boolean { + override fun containsKey(key: K): Boolean { return implFindEntry(key, false) != null } - override fun containsValue(value: Any): Boolean { - for ((key, v) in entries) { - if (value == v) { - return true - } - } - return false - } + override fun containsValue(value: V): Boolean = entries.any { it.value == value } - internal fun containsEntry(entry: Entry<*, *>): Boolean { + internal fun containsEntry(entry: Map.Entry<*, *>): Boolean { val key = entry.key val value = entry.value val ourValue = get(key) @@ -123,66 +83,40 @@ abstract class AbstractMap protected constructor() : MutableMap { return true } - override fun equals(obj: Any?): Boolean { - if (obj === this) { - return true - } - if (obj !is Map<*, *>) { - return false - } - if (size != obj.size) { - return false - } + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Map<*, *>) return false + if (size != other.size) return false - for (entry in obj.entries) { - if (!containsEntry(entry)) { - return false - } - } - return true + return other.entries.all { containsEntry(it) } } - override operator fun get(key: Any): V? { - return getEntryValueOrNull(implFindEntry(key, false)) - } + override operator fun get(key: K): V? = implFindEntry(key, false)?.value - override fun hashCode(): Int { - return Collections.hashCode(entries) - } + override fun hashCode(): Int = entries.hashCode() - override fun isEmpty(): Boolean { - return size == 0 - } + override fun isEmpty(): Boolean = size == 0 + override val size: Int get() = entries.size - override fun keySet(): Set { + + override val keys: MutableSet get() { return object : AbstractSet() { override fun clear() { this@AbstractMap.clear() } - override operator fun contains(key: Any?): Boolean { - return containsKey(key) - } + override operator fun contains(key: K): Boolean = containsKey(key) - override operator fun iterator(): Iterator { + override operator fun iterator(): MutableIterator { val outerIter = entries.iterator() - return object : Iterator { - override fun hasNext(): Boolean { - return outerIter.hasNext() - } - - override fun next(): K { - val entry = outerIter.next() - return entry.key - } - - override fun remove() { - outerIter.remove() - } + return object : MutableIterator { + override fun hasNext(): Boolean = outerIter.hasNext() + override fun next(): K = outerIter.next().key + override fun remove() = outerIter.remove() } } - override fun remove(key: Any?): Boolean { + override fun remove(key: K): Boolean { if (containsKey(key)) { this@AbstractMap.remove(key) return true @@ -190,89 +124,63 @@ abstract class AbstractMap protected constructor() : MutableMap { return false } - override fun size(): Int { - return this@AbstractMap.size - } + override val size: Int get() = this@AbstractMap.size } } - override fun put(key: K, value: V): V { + override fun put(key: K, value: V): V? { throw UnsupportedOperationException("Put not supported on this map") } override fun putAll(map: Map) { - checkNotNull(map) for ((key, value) in map) { put(key, value) } } - override fun remove(key: Any): V { - return getEntryValueOrNull(implFindEntry(key, true)) - } + override fun remove(key: K): V? = implFindEntry(key, true)?.value - override fun size(): Int { - return entries.size - } + override fun toString(): String = entries.joinToString(", ", "{", "}") { toString(it) } - override fun toString(): String { - val joiner = StringJoiner(", ", "{", "}") - for (entry in entries) { - joiner.add(toString(entry)) - } - return joiner.toString() - } + private fun toString(entry: Map.Entry): String = toString(entry.key) + "=" + toString(entry.value) - private fun toString(entry: Entry): String { - return toString(entry.key) + "=" + toString(entry.value) - } + private fun toString(o: Any?): String = if (o === this) "(this Map)" else o.toString() - private fun toString(o: Any): String { - return if (o === this) "(this Map)" else o.toString() - } - - override fun values(): Collection { + override val values: MutableCollection get() { return object : AbstractCollection() { - override fun clear() { - this@AbstractMap.clear() - } + override fun clear() = this@AbstractMap.clear() - override operator fun contains(value: Any?): Boolean { - return containsValue(value) - } + override operator fun contains(value: V): Boolean = containsValue(value) - override operator fun iterator(): Iterator { + override operator fun iterator(): MutableIterator { val outerIter = entries.iterator() - return object : Iterator { - override fun hasNext(): Boolean { - return outerIter.hasNext() - } - - override fun next(): V { - val entry = outerIter.next() - return entry.value - } - - override fun remove() { - outerIter.remove() - } + return object : MutableIterator { + override fun hasNext(): Boolean = outerIter.hasNext() + override fun next(): V = outerIter.next().value + override fun remove() = outerIter.remove() } } - override fun size(): Int { - return this@AbstractMap.size + override val size: Int get() = this@AbstractMap.size + + // TODO: should we implement them this way? Currently it's unspecified in JVM + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Collection<*>) return false + return AbstractList.orderedEquals(this, other) } + override fun hashCode(): Int = AbstractList.orderedHashCode(this) } } - private fun implFindEntry(key: Any, remove: Boolean): Entry? { + private fun implFindEntry(key: K, remove: Boolean): Map.Entry? { val iter = entries.iterator() while (iter.hasNext()) { var entry = iter.next() val k = entry.key if (key == k) { if (remove) { - entry = SimpleEntry(entry.key, entry.value) + entry = SimpleEntry(entry) iter.remove() } return entry @@ -283,12 +191,11 @@ abstract class AbstractMap protected constructor() : MutableMap { companion object { - internal fun getEntryKeyOrNull(entry: Entry?): K? { - return if (entry == null) null else entry!!.key - } - - internal fun getEntryValueOrNull(entry: Entry?): V? { - return if (entry == null) null else entry!!.value + internal fun entryHashCode(e: Map.Entry<*, *>): Int = with(e) { (key?.hashCode() ?: 0) xor (value?.hashCode() ?: 0) } + internal fun entryToString(e: Map.Entry<*, *>): String = with(e) { "$key=$value" } + internal fun entryEquals(e: Map.Entry<*, *>, other: Any?): Boolean { + if (other !is Map.Entry<*, *>) return false + return e.key == other.key && e.value == other.value } } }