Running this example code (from Cay Horstmann)

ConcurrentHashMap<String,LongAdder> counts = new ConcurrentHashMap<>();
for (String key : "Row, row, row a boat".split("\\PL+"))
    counts.computeIfAbsent(key, k -> new LongAdder()).increment();
System.out.println(counts);

The output is

{a=1, Row=1, row=2, boat=1}

I am surprised to see row=2 because I expected computeIfAbsent() would increment only when it found the first "row" and not the second one.

🟢 Solution
5

counts.computeIfAbsent(key, k -> new LongAdder())

This entire expression can go one of two ways:

  1. key is already in the counts map. In this case, the 'computer' function (k -> new LongAdder()) is completely ignored; it isn't run at all. Instead, the value associated with that key is returned.

  2. key is not in the counts map. In this case, the 'computer' function is executed once, and the value it returns is now associated with the key.

In other words, it's identical to:

LongAdder result;
if (counts.containsKey(key)) result = counts.get(key);
else {
  result = new LongAdder();
  counts.put(key, result);
}
return result;

Which is a mouthful, potentially less efficient, and potentially non-atomic whereas .computerIfAbsent can be atomic if you use the right implementation (such as ConcurrentHashMap).

Here's the clue: You then invoke .increment() on this result, regardless of whether we're in the #1 case or the #2 case.

In other words:

First 'row' is seen, computeIfAbsent ends up executing the lambda and thus, runs something like counts.put(k, new LongAdder()) and THEN you call .increment() on this.

Next, the second 'row' is seen, computeIfAbsent ends up being equivalent to counts.get(key), and you call .increment() on that. Which is the same LongAdder we made when we saw the first 'row', and thus this is the second time you are invoking increment() on it.

It's not computeIfAbsent that's incrementing anything. It's the .increment() that increments something.