Map compute(), merge(), computeIfAbsent() — Atomic Map Operations
Fast recall
Quick revision
Core answer
map.computeIfAbsent(key, k -> new ArrayList<>()).add(value). `computeIfPresent(key, remappingFunction)` — if key present, recompute value (or remove if null returned). `compute(key, remappingFunction)` — recompute regardless of presence (function gets old value or null). `merge(key, value, remappingFunction)` — if absent, put value; if present, apply function to combine: map.merge(word, 1, Integer::sum) is the idiomatic word-counter. Why they matter: (1) atomic in `ConcurrentHashMap` — no lost updates from concurrent get-check-put races; (2) less code than if (map.containsKey(k)) map.put(k, ...) else map.put(k, ...); (3) cleaner intent — the operation says what it does. Pitfalls: (1) not atomic in non-concurrent maps (HashMap) — only the API is single-call, not thread-safe; (2) returning null from a remapping function removes the key — surprising; (3) don't recursively modify the map inside the function — undefined behavior; (4) `merge` doesn't handle null values the same way as compute — read the Javadoc. For interviews: lead with 'replaces get-check-put, atomic in ConcurrentHashMap, idiomatic for multimap lazy-init and counters'.Understanding of atomic map mutation methods and when they replace check-then-act patterns
Using get() + put() instead of computeIfAbsent() for lazy initialization, introducing race conditions in concurrent maps
Show that compute/merge are atomic in ConcurrentHashMap, eliminating the need for external synchronization in common patterns like counters and caches
Interview-ready guide
Interview answer
computeIfAbsent() and merge() are Map methods added in Java 8 that replace common multi-step patterns — grouping items into lists and aggregating counters — with a single atomic, readable operation that eliminates the verbose get-check-put boilerplate.
computeIfAbsent():
- Returns existing value or computes and stores a new one if absent — one line instead of four
- Perfect for lazy initialization of multi-value maps:
map.computeIfAbsent(key, k -> new ArrayList<>()).add(value) - In ConcurrentHashMap: executes within the bucket lock — atomic, no race condition
merge():
merge(word, 1, Integer::sum): stores 1 if absent, increments the existing value if present- Perfect for counters and aggregation — replaces the null-check-then-put pattern
- In ConcurrentHashMap: also atomic within the bucket lock
Critical gotcha:
- Returning null from the remapping function removes the entry — intentional for conditional removal, but if your function accidentally returns null, the entry silently disappears
I recommend computeIfAbsent() for lazy initialization and merge() for counters — cleaner, safer, and atomic in concurrent maps.
Map compute(), merge(), computeIfAbsent() — Atomic Map Operations
Direct Answer
compute, merge, computeIfAbsent, and computeIfPresent are Map methods (Java 8+) that atomically combine get + check + put into a single operation, replacing the verbose get-check-put anti-pattern. computeIfAbsent(key, mappingFunction) — if key absent, compute value and put; most-used for lazy initialization of multi-valued maps: map.computeIfAbsent(key, k -> new ArrayList<>()).add(value). computeIfPresent(key, remappingFunction) — if key present, recompute value (or remove if null returned). compute(key, remappingFunction) — recompute regardless of presence (function gets old value or null). merge(key, value, remappingFunction) — if absent, put value; if present, apply function to combine: map.merge(word, 1, Integer::sum) is the idiomatic word-counter. Why they matter: (1) atomic in ConcurrentHashMap — no lost updates from concurrent get-check-put races; (2) less code than if (map.containsKey(k)) map.put(k, ...) else map.put(k, ...); (3) cleaner intent — the operation says what it does. Pitfalls: (1) not atomic in non-concurrent maps (HashMap) — only the API is single-call, not thread-safe; (2) returning null from a remapping function removes the key — surprising; (3) don't recursively modify the map inside the function — undefined behavior; (4) merge doesn't handle null values the same way as compute — read the Javadoc. For interviews: lead with 'replaces get-check-put, atomic in ConcurrentHashMap, idiomatic for multimap lazy-init and counters'.
Deep Dive & Architecture
compute, merge, computeIfAbsent, and computeIfPresent are Map methods (Java 8+) that atomically combine get + check + put into a single operation, replacing the verbose get-check-put anti-pattern. computeIfAbsent(key, mappingFunction) — if key absent, compute value and put; most-used for lazy initialization of multi-valued maps: map.computeIfAbsent(key, k -> new ArrayList<>()).add(value). computeIfPresent(key, remappingFunction) — if key present, recompute value (or remove if null returned). compute(key, remappingFunction) — recompute regardless of presence (function gets old value or null). merge(key, value, remappingFunction) — if absent, put value; if present, apply function to combine: map.merge(word, 1, Integer::sum) is the idiomatic word-counter. Why they matter: (1) atomic in ConcurrentHashMap — no lost updates from concurrent get-check-put races; (2) less code than if (map.containsKey(k)) map.put(k, ...) else map.put(k, ...); (3) cleaner intent — the operation says what it does. Pitfalls: (1) not atomic in non-concurrent maps (HashMap) — only the API is single-call, not thread-safe; (2) returning null from a remapping function removes the key — surprising; (3) don't recursively modify the map inside the function — undefined behavior; (4) merge doesn't handle null values the same way as compute — read the Javadoc. For interviews: lead with 'replaces get-check-put, atomic in ConcurrentHashMap, idiomatic for multimap lazy-init and counters'.
The most common Map pattern in Java is check-then-act: get the value, check if it exists, compute a new value, put it back. This pattern has two problems. In single-threaded code, it is verbose — four lines for what should be one. In concurrent code, it is broken — another thread can modify the map between get() and put(). Java 8 introduced compute(), computeIfAbsent(), computeIfPresent(), and merge() to replace this pattern.
A common mistake is confusing the interface with the implementation (e.g., saying List when you mean ArrayList), or not knowing that Map does not extend Collection.
Common Mistakes & Pitfalls
Using get() + put() instead of computeIfAbsent() for lazy initialization, introducing race conditions in concurrent maps
Check your understanding
Answer these without looking back at the article.
- What is the Java Collections Framework (JCF) and why does it exist? It provides a unified architecture of interfaces and implementations for storing and manipulating groups of objects, replacing the ad-hoc pre-JCF classes (Vector, Hashtable, Enumeration) with a consistent, interoperable API.
- How does HashSet work internally in Java?
- How does HashMap internally handle hash collisions in Java 8+? Explain the transition from linked list to tree.
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.