computeifabsent Example in Java map
Method Signature
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
- key — the key to look up
- mappingFunction — receives the key; its return value is inserted as the new value
- Returns — the existing value if the key was already present, or the newly computed value
- Side effect — if the function returns
null, no entry is inserted
Basic Example
import java.util.HashMap;
import java.util.Map;
public class ComputeIfAbsentExample {
public static void main(String[] args) {
Map<Integer, String> numbers = new HashMap<>();
numbers.put(1, "one");
numbers.put(2, "two");
numbers.put(3, "three");
System.out.println("Before: " + numbers);
// Key 4 is absent — value will be computed and inserted
numbers.computeIfAbsent(4, k -> "four");
// Key 5 is absent — lambda shorthand
numbers.computeIfAbsent(5, k -> "five");
// Key 1 already exists — no change
numbers.computeIfAbsent(1, k -> "ONE");
System.out.println("After: " + numbers);
}
}
Output:
Before: {1=one, 2=two, 3=three}
After: {1=one, 2=two, 3=three, 4=four, 5=five}
Key 1 remains "one" — computeIfAbsent() never overwrites existing values.
Common Pattern: Map of Lists
computeIfAbsent() is especially useful for building a Map<K, List<V>> — the classic "group by" pattern:
import java.util.*;
public class GroupByExample {
public static void main(String[] args) {
List<String> words = List.of("apple", "ant", "banana", "avocado", "blueberry");
Map<Character, List<String>> grouped = new HashMap<>();
for (String word : words) {
char firstLetter = word.charAt(0);
// Create the list if this letter hasn't been seen yet
grouped.computeIfAbsent(firstLetter, k -> new ArrayList<>()).add(word);
}
grouped.forEach((letter, list) ->
System.out.println(letter + ": " + list));
}
}
Output:
a: [apple, ant, avocado]
b: [banana, blueberry]
Without computeIfAbsent(), you'd need to check whether the list exists, create it if not, then add — three lines instead of one.
Before vs. After Java 8
Old approach:
if (!grouped.containsKey(firstLetter)) {
grouped.put(firstLetter, new ArrayList<>());
}
grouped.get(firstLetter).add(word);
With computeIfAbsent():
grouped.computeIfAbsent(firstLetter, k -> new ArrayList<>()).add(word);
Related Map Compute Methods
| Method | Behavior |
|---|---|
computeIfAbsent(k, fn) | Insert only if key is absent |
computeIfPresent(k, fn) | Update only if key exists |
compute(k, fn) | Always compute; handles both cases |
getOrDefault(k, default) | Read-only; returns default without inserting |
putIfAbsent(k, v) | Insert a fixed value if absent (no function) |
Memoization with computeIfAbsent()
computeIfAbsent() is perfect for caching computed results — only run the expensive computation once per key:
Map<Integer, Long> fibCache = new HashMap<>();
long fibonacci(int n) {
if (n <= 1) return n;
return fibCache.computeIfAbsent(n, k -> fibonacci(k - 1) + fibonacci(k - 2));
}
System.out.println(fibonacci(40)); // 102334155
The function is called at most once per key. On subsequent calls with the same key, the cached value is returned immediately. This turns an exponential recursion into a linear one.
Thread-Safe Use with ConcurrentHashMap
computeIfAbsent() is atomic on ConcurrentHashMap — the check and insertion happen as a single operation. This is important in concurrent environments where two threads might try to initialize the same key simultaneously:
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap<String, List<String>> groupedResults = new ConcurrentHashMap<>();
// Safe to call from multiple threads simultaneously
groupedResults.computeIfAbsent("category-A", k -> new ArrayList<>()).add("item1");
groupedResults.computeIfAbsent("category-A", k -> new ArrayList<>()).add("item2");
With a regular HashMap, this pattern is not thread-safe — two threads could both call the mapping function and the second would overwrite the first's result. Always use ConcurrentHashMap for shared maps accessed from multiple threads.
computeIfAbsent() vs putIfAbsent()
Both insert a value only if the key is absent, but there's an important difference:
// putIfAbsent — the new ArrayList is ALWAYS created, even if the key exists
map.putIfAbsent(key, new ArrayList<>());
// computeIfAbsent — the lambda is ONLY called if the key is absent
map.computeIfAbsent(key, k -> new ArrayList<>());
| Method | When is value computed? | Takes a function? |
|---|---|---|
computeIfAbsent(k, fn) | Only if key absent | Yes — receives the key |
putIfAbsent(k, v) | Always (value evaluated before call) | No — fixed value |
For values that are cheap to create (like a constant string), putIfAbsent() is fine. For values that are expensive to create (like a new database connection or a populated list), always use computeIfAbsent() to avoid unnecessary work.
Summary
computeIfAbsent() shines when you need lazy initialization of map values — especially for Map<K, List<V>> or Map<K, Set<V>> grouping patterns and for memoization caches. Prefer it over putIfAbsent() when the value is expensive to create. Use it with ConcurrentHashMap for thread-safe lazy initialization.