Prepare to treat keys mapped to null same way as missing keys in Map extensions: getOrElse, getOrPut, getOrImplicitDefault.
This commit is contained in:
@@ -35,3 +35,15 @@ public fun <T> setOf(element: T): Set<T> = hashSetOf(element)
|
||||
* specified value.
|
||||
*/
|
||||
public fun <K, V> mapOf(pair: Pair<K, V>): Map<K, V> = hashMapOf(pair)
|
||||
|
||||
|
||||
public inline fun <K, V> MutableMap<K, V>.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
|
||||
}
|
||||
}
|
||||
@@ -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 <K, V: Any> Map<K, V>.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 <K, V> Map<K, V>.getOrImplicitDefault(key: K): V {
|
||||
if (this is MapWithDefault)
|
||||
return this.getOrImplicitDefault(key)
|
||||
|
||||
@@ -179,6 +179,7 @@ public fun <K, V> Map.Entry<K, V>.toPair(): Pair<K, V> = 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 <K, V> Map<K, V>.getOrElse(key: K, defaultValue: () -> V): V {
|
||||
val value = get(key)
|
||||
if (value == null && !containsKey(key)) {
|
||||
@@ -194,14 +195,30 @@ public inline fun <K, V> Map<K, V>.getOrElse(key: K, defaultValue: () -> V): V {
|
||||
*
|
||||
* @sample test.collections.MapTest.getOrPut
|
||||
*/
|
||||
public inline fun <K, V> MutableMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
|
||||
@kotlin.jvm.JvmVersion
|
||||
public inline fun <K, V: Any> MutableMap<K, V>.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 <K, V> MutableMap<K, V>.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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public fun <K, V> MutableMap<K, V>.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 <K, V> ConcurrentMap<K, V>.getOrPut(key: K, defaultValue: () -> V): Nothing =
|
||||
throw UnsupportedOperationException("getOrPut is not supported on ConcurrentMap.")
|
||||
|
||||
@@ -39,13 +40,20 @@ public inline fun <K, V> ConcurrentMap<K, V>.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 <K, V: Any> ConcurrentMap<K, V>.concurrentGetOrPut(key: K, defaultValue: () -> V): V {
|
||||
public inline fun <K, V: Any> ConcurrentMap<K, V>.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 <K, V: Any> ConcurrentMap<K, V>.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.
|
||||
*
|
||||
|
||||
@@ -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<String, Int>()
|
||||
|
||||
// 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<String, Int>).getOrPut("x") { 1 }
|
||||
|
||||
@@ -10,6 +10,8 @@ class MapTest {
|
||||
val data = mapOf<String, Int>()
|
||||
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<String, Int?>()
|
||||
val c = empty.getOrPut("") { null }
|
||||
assertEquals(null, c)
|
||||
|
||||
val d = empty.getOrPut("") { 1 }
|
||||
assertEquals(null, d) // soon will change to 1
|
||||
}
|
||||
|
||||
@test fun sizeAndEmpty() {
|
||||
|
||||
Reference in New Issue
Block a user