Map.putIfAbsent associates a value when a key has no non-null mapping and returns the previous value. Its treatment of null is easy to misread: an existing null mapping is treated as absent, but passing null as the new value can create a null mapping. section 8.4
A repeated call can change the map
var map = new HashMap<String, String>(); map.putIfAbsent("x", null); map.putIfAbsent("x", "1"); The first call can create a null mapping. Because putIfAbsent treats that mapping as absent, the second call replaces it with "1". Therefore, calling the method twice with the same key does not guarantee that the second call has no effect. section 8.4
var map = new HashMap<String, String>();
map.putIfAbsent("x", null); #1
System.out.println(map); #2
map.putIfAbsent("x", "1"); #3
System.out.println(map); #4
Inspect the minimal HashMap sequence that exposes putIfAbsent's two different null behaviors. section 8.4
Use the method deliberately:
- Avoid null values when possible; putIfAbsent, merge, compute, and computeIfAbsent follow different rules for existing null mappings and null results. section 8.4
- For concurrently updated maps, prefer a single atomic Map operation instead of a separate get followed by put; use merge for occurrence counting. section 9.12
