diff --git a/libraries/stdlib/js/src/kotlin/collections/AbstractMutableMap.kt b/libraries/stdlib/js/src/kotlin/collections/AbstractMutableMap.kt index f705221f119..f0dad87c625 100644 --- a/libraries/stdlib/js/src/kotlin/collections/AbstractMutableMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/AbstractMutableMap.kt @@ -50,6 +50,8 @@ public actual abstract class AbstractMutableMap protected actual construct 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 fun clear() { diff --git a/libraries/stdlib/js/src/kotlin/collections/HashMap.kt b/libraries/stdlib/js/src/kotlin/collections/HashMap.kt index c372f056592..82972a348b9 100644 --- a/libraries/stdlib/js/src/kotlin/collections/HashMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/HashMap.kt @@ -32,7 +32,7 @@ public actual open class HashMap : AbstractMutableMap, MutableMap> = internalMap.iterator() - override fun remove(element: MutableEntry): Boolean { + override fun removeEntry(element: Map.Entry): Boolean { if (contains(element)) { this@HashMap.remove(element.key) return true diff --git a/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt b/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt index a626e23b37d..c06912205cf 100644 --- a/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt @@ -90,7 +90,7 @@ public actual open class LinkedHashMap : HashMap, MutableMap { override operator fun iterator(): MutableIterator> = EntryIterator() - override fun remove(element: MutableEntry): Boolean { + override fun removeEntry(element: Map.Entry): Boolean { checkIsMutable() if (contains(element)) { this@LinkedHashMap.remove(element.key) diff --git a/libraries/stdlib/test/collections/MapTest.kt b/libraries/stdlib/test/collections/MapTest.kt index 3d57501c20b..6d4021f0fd9 100644 --- a/libraries/stdlib/test/collections/MapTest.kt +++ b/libraries/stdlib/test/collections/MapTest.kt @@ -420,7 +420,10 @@ class MapTest { // which [contains] method takes [MutableEntry], so the compiler may generate special bridge // returning false for values that aren't [MutableEntry] (including [SimpleEntry]). assertTrue(map.entries.contains(SimpleEntry(key, value)), mapDescription) - assertTrue(map.entries.toSet().contains(SimpleEntry(key, value)), mapDescription) + assertTrue(map.entries.toSet().contains(SimpleEntry(key, value)), "$mapDescription: reference") + + assertFalse(map.entries.contains(null as Any?), "$mapDescription: contains null") + assertFalse(map.entries.contains("not an entry" as Any?), "$mapDescription: contains not an entry") } val mapLetterToIndex = ('a'..'z').mapIndexed { i, c -> "$c" to i }.toMap() @@ -436,6 +439,36 @@ class MapTest { doTest("built Map", builtMap, "y", 24) } + @Test + fun entriesCovariantRemove() { + fun doTest(implName: String, map: MutableMap, key: String, value: Int) { + class SimpleEntry(override val key: K, override val value: V) : Map.Entry { + override fun toString(): String = "$key=$value" + override fun hashCode(): Int = key.hashCode() xor value.hashCode() + override fun equals(other: Any?): Boolean = + other is Map.Entry<*, *> && key == other.key && value == other.value + } + + val mapDescription = "$implName: ${map::class}" + + assertTrue(map.entries.toMutableSet().remove(SimpleEntry(key, value) as Map.Entry<*, *>), "$mapDescription: reference") + assertTrue(map.entries.remove(SimpleEntry(key, value) as Map.Entry<*, *>), mapDescription) + + assertFalse(map.entries.remove(null as Any?), "$mapDescription: remove null") + assertFalse(map.entries.remove("not an entry" as Any?), "$mapDescription: remove not an entry") + } + + val mapLetterToIndex = ('a'..'z').mapIndexed { i, c -> "$c" to i }.toMap() + doTest("default mutable", mapLetterToIndex.toMutableMap(), "b", 1) + doTest("HashMap", mapLetterToIndex.toMap(HashMap()), "c", 2) + doTest("LinkedHashMap", mapLetterToIndex.toMap(LinkedHashMap()), "d", 3) + + val builtMap = buildMap { + putAll(mapLetterToIndex) + doTest("MapBuilder", this, "z", 25) + } + } + @Test fun firstNotNullOf() { val map = mapOf("Alice" to 20, "Tom" to 13, "Bob" to 18)