Search code examples
kotlinconcurrenthashmap

Should we use computeIfAbsent instead of getOrPut?


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:

  1. getOrPut can put a null value into the entry, whereas computeIfAbsent doesn't support it.

  2. getOrPut does not guarantee that the default function will only be called once, whereas Java's computeIfAbsent does guarantee that.

  3. 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"?


Solution

  • You have pointed some very interesting facts for both methods.

    Please consider an additional one which will be a reason to use getOrPut.

    1. 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.