In his talk about Effective Java at 54:15 Joshua Bloch recommends to use get before putIfAbsent in order to improve performance and concurrency. This leads me to the question why this optimization is already not build in like
public V BETTER_putIfAbsent(K key, V value) {
V result = get(key);
if (result!=null) return result;
return ORIGINAL_putIfAbsent(key, value);
}
This adds a double checked locking, the transactional semantics remains the same; so it is not wrong.
Whether it is actually an optimization depends on usage. We are always tempted to check for special cases that we know cheaper solutions exist
This may work, or not – if
Ais rarelytrue, the additional check costs more than it saves. (and when A indeed is true on occasion, it can disrupt CPU branch prediction, it could have been cheaper to always go with the generic solution even when A=true)In Joshua’s example,
Aindeed is true frequently. He must be requesting intern string for the same string(same in value, not in identity) many times, therefore in most calls the map already has the key.If every call to
intern()gets a different string, then the map never has the key, and his optimization backfires – the “optimization” costs more time, and saves none.Of course, when it comes to string intern, the 1st case is more realistic in practice .
In general though,
putIfAbsent()cannot predict how it’s being used, so it’s unwise to include this special case “optimization” inside it. In many use cases, contention is low, the map most likely doesn’t have the key whenputIfAbsentis called, it would be wrong in these cases if the “optimzation” is built in.