diff --git a/js/js.libraries/src/core/collections.kt b/js/js.libraries/src/core/collections.kt index 3e8e5b36c97..32ba6828312 100644 --- a/js/js.libraries/src/core/collections.kt +++ b/js/js.libraries/src/core/collections.kt @@ -35,3 +35,15 @@ public fun setOf(element: T): Set = hashSetOf(element) * specified value. */ public fun mapOf(pair: Pair): Map = hashMapOf(pair) + + +public inline fun MutableMap.getOrPut(key: K, defaultValue: () -> V): V { + val value = get(key) + return if (value == null && !containsKey(key)) { + val answer = defaultValue() + put(key, answer) + answer + } else { + value as V + } +} \ No newline at end of file diff --git a/libraries/stdlib/src/kotlin/collections/MapWithDefault.kt b/libraries/stdlib/src/kotlin/collections/MapWithDefault.kt index 9eeda8c7a1a..cd71bab0312 100644 --- a/libraries/stdlib/src/kotlin/collections/MapWithDefault.kt +++ b/libraries/stdlib/src/kotlin/collections/MapWithDefault.kt @@ -12,6 +12,15 @@ import java.util.* * * @throws NoSuchElementException when the map doesn't contain value for the specified key and no implicit default was provided for that map. */ +public fun Map.getOrImplicitDefault(key: K): V { + if (this is MapWithDefault) + return this.getOrImplicitDefault(key) + + return getOrElse(key, { throw NoSuchElementException("Key $key is missing in the map.") }) +} + +@kotlin.jvm.JvmName("getOrImplicitDefaultNullable") +@Deprecated("This function will change its behavior soon not to distinguish missing keys and keys mapped to nulls.") public fun Map.getOrImplicitDefault(key: K): V { if (this is MapWithDefault) return this.getOrImplicitDefault(key) diff --git a/libraries/stdlib/src/kotlin/collections/Maps.kt b/libraries/stdlib/src/kotlin/collections/Maps.kt index daf3749c151..123880638bf 100644 --- a/libraries/stdlib/src/kotlin/collections/Maps.kt +++ b/libraries/stdlib/src/kotlin/collections/Maps.kt @@ -179,6 +179,7 @@ public fun Map.Entry.toPair(): Pair = Pair(key, value) * * @sample test.collections.MapTest.getOrElse */ +@Deprecated("This function will change its behavior soon not to distinguish missing keys and keys mapped to nulls. To stick with the new behavior you can use get(key) with ?: operator after instead.") public inline fun Map.getOrElse(key: K, defaultValue: () -> V): V { val value = get(key) if (value == null && !containsKey(key)) { @@ -194,14 +195,30 @@ public inline fun Map.getOrElse(key: K, defaultValue: () -> V): V { * * @sample test.collections.MapTest.getOrPut */ -public inline fun MutableMap.getOrPut(key: K, defaultValue: () -> V): V { +@kotlin.jvm.JvmVersion +public inline fun MutableMap.getOrPut(key: K, defaultValue: () -> V): V { val value = get(key) - if (value == null && !containsKey(key)) { + return if (value == null) { val answer = defaultValue() put(key, answer) - return answer + answer } else { - return value as V + value + } +} + +@kotlin.jvm.JvmName("getOrPutNullable") +@kotlin.jvm.JvmVersion +@Deprecated("This function will change its behavior soon not to distinguish missing keys and keys mapped to nulls.") +@kotlin.internal.LowPriorityInOverloadResolution +public inline fun MutableMap.getOrPut(key: K, defaultValue: () -> V): V { + val value = get(key) + return if (value == null && !containsKey(key)) { + val answer = defaultValue() + put(key, answer) + answer + } else { + value as V } } diff --git a/libraries/stdlib/src/kotlin/collections/MapsJVM.kt b/libraries/stdlib/src/kotlin/collections/MapsJVM.kt index a732c63fa2e..bba288da13f 100644 --- a/libraries/stdlib/src/kotlin/collections/MapsJVM.kt +++ b/libraries/stdlib/src/kotlin/collections/MapsJVM.kt @@ -27,6 +27,7 @@ public fun MutableMap.set(key: K, value: V): V? = put(key, value) * Use [concurrentGetOrPut] instead, or cast this to a [MutableMap] if you want to sacrifice the concurrent-safety. */ @Deprecated("Use concurrentGetOrPut instead or cast this map to MutableMap.", level = DeprecationLevel.ERROR) +@kotlin.internal.LowPriorityInOverloadResolution public inline fun ConcurrentMap.getOrPut(key: K, defaultValue: () -> V): Nothing = throw UnsupportedOperationException("getOrPut is not supported on ConcurrentMap.") @@ -39,13 +40,20 @@ public inline fun ConcurrentMap.getOrPut(key: K, defaultValue: () - * This method guarantees not to put the value into the map if the key is already there, * but the [defaultValue] function may be invoked even if the key is already in the map. */ -public inline fun ConcurrentMap.concurrentGetOrPut(key: K, defaultValue: () -> V): V { +public inline fun ConcurrentMap.getOrPut(key: K, defaultValue: () -> V): V { // Do not use computeIfAbsent on JVM8 as it would change locking behavior return this.get(key) ?: defaultValue().let { default -> this.putIfAbsent(key, default) ?: default } } +@Deprecated("Use getOrPut instead", ReplaceWith("getOrPut(key, defaultValue)")) +public inline fun ConcurrentMap.concurrentGetOrPut(key: K, defaultValue: () -> V): V { + // Do not use computeIfAbsent on JVM8 as it would change locking behavior + return this.get(key) ?: + defaultValue().let { default -> this.putIfAbsent(key, default) ?: default } +} + /** * Converts this [Map] to a [SortedMap] so iteration order will be in key order. * diff --git a/libraries/stdlib/test/collections/MapJVMTest.kt b/libraries/stdlib/test/collections/MapJVMTest.kt index c0354c9b6e2..d6cc6b6759c 100644 --- a/libraries/stdlib/test/collections/MapJVMTest.kt +++ b/libraries/stdlib/test/collections/MapJVMTest.kt @@ -1,6 +1,7 @@ package test.collections import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap import kotlin.test.assertEquals import kotlin.test.assertFails import kotlin.test.expect @@ -51,14 +52,13 @@ class MapJVMTest { assertEquals(listOf(1, 3, 5), map.keys.toList()) assertEquals(listOf('b', 'd', 'f'), map.values.toList()) } - + @test fun getOrPutFailsOnConcurrentMap() { val map = ConcurrentHashMap() - // now this is an error - // map.getOrPut("x") { 1 } + // not an error anymore expect(1) { - map.concurrentGetOrPut("x") { 1 } + map.getOrPut("x") { 1 } } expect(1) { (map as MutableMap).getOrPut("x") { 1 } diff --git a/libraries/stdlib/test/collections/MapTest.kt b/libraries/stdlib/test/collections/MapTest.kt index ffc331949e0..39e3f20c187 100644 --- a/libraries/stdlib/test/collections/MapTest.kt +++ b/libraries/stdlib/test/collections/MapTest.kt @@ -10,6 +10,8 @@ class MapTest { val data = mapOf() val a = data.getOrElse("foo") { 2 } assertEquals(2, a) + val a1 = data.getOrElse("foo") { data.get("bar") } ?: 1 + assertEquals(1, a1) val b = data.getOrElse("foo") { 3 } assertEquals(3, b) @@ -54,6 +56,9 @@ class MapTest { val empty = hashMapOf() val c = empty.getOrPut("") { null } assertEquals(null, c) + + val d = empty.getOrPut("") { 1 } + assertEquals(null, d) // soon will change to 1 } @test fun sizeAndEmpty() {