Kotlin has had an extension function ConcurrentMap.getOrPut
from the beginning (1.0). This has the same signature as Java's computeIfAbsent
and does a similar thing, but with some subtle differences:
getOrPut
can put a null value into the entry, whereas computeIfAbsent
doesn't support it.
getOrPut
does not guarantee that the default function will only be called once, whereas Java's computeIfAbsent
does guarantee that.
When getOrPut
was released, the latest Java version was Java 8. In Java 8, computeIfAbsent
always locked the entry even if the value was already there. Therefore, Kotlin may have had better performance because it doesn't lock when it doesn't need to.
However, in recent versions of Java (I've just checked 17) the locking overhead mentioned in point 3 is no longer there. So Kotlin's getOrPut
no longer has that advantage, but it has the disadvantage of point 2. Under what circumstances should we continue to use getOrPut
nowadays (assuming we don't want to put a null into a map) or should it now be considered "legacy"?
You have pointed some very interesting facts for both methods.
Please consider an additional one which will be a reason to use getOrPut
.
ComputeIfAbsent
ensures atomicity. This means that this method can and will block other threads during computation to ensure atomicity.From doc
If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unless null. The entire method invocation is performed atomically, so the function is applied at most once per key. Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map.
But getOrPut
does not ensure atomicity.
From doc
Note that the operation is not guaranteed to be atomic if the map is being modified concurrently.
This would mean that in a use case scenario, where atomicity is not important to you, getOrPut
will offer performance benefits in comparison with computeIfAbsent
.