Map API essentials in Java
getOrDefault, putIfAbsent, computeIfAbsent, merge, entrySet iteration.
counts.merge(w, 1, Integer::sum). Let's unlock the Map power tools.A safer get
**getOrDefault(k, d) returns the value for k, or the fallback d when k is missing. It only reads**: it never inserts anything into the map.
Map<String, Integer> stock = new HashMap<>();
stock.put("apple", 3);
stock.getOrDefault("apple", 0); // 3
stock.getOrDefault("kiwi", 0); // 0Your turn
What does this print?
Map<String, Integer> m = new HashMap<>();
m.put("tea", 2);
int c = m.getOrDefault("coffee", 0);
System.out.println(c + " " + m.size());0 20 1null 1
Show the answer
coffee is missing, so you get the fallback 0, but nothing is added. The map still has just one entry.
putIfAbsent vs computeIfAbsent
**putIfAbsent(k, v) writes only if k is missing; an existing value stays untouched. It returns the previous value (null if there was none). computeIfAbsent(k, f) builds the value only when needed and returns the current value**, new or existing.
m.put("k", "old");
m.putIfAbsent("k", "new"); // stays "old"
m.putIfAbsent("z", "zed"); // adds zGrouping the wrong way
Grouping into a map of lists with putIfAbsent crashes the first time a key is seen: it returns the previous value, which is null, and .add throws NullPointerException. computeIfAbsent returns the list itself, so you can add to it straight away.
// ✗ NPE on a new key:
g.putIfAbsent(k, new ArrayList<>()).add(v);
// ✓ always returns the list:
g.computeIfAbsent(k, x -> new ArrayList<>())
.add(v);merge: the counter's best friend
**merge(k, v, fn)**: if k is missing, store v. If present, store fn(old, v). With Integer::sum that's a counter: the first sighting stores 1, later ones add 1.
counts.merge("hi", 1, Integer::sum); // hi=1
counts.merge("hi", 1, Integer::sum); // hi=2Count the words
What does this print?
Map<String, Integer> counts = new TreeMap<>();
for (String w : "x y x z y x".split(" ")) {
counts.merge(w, 1, Integer::sum);
}
System.out.println(counts);{x=1, y=1, z=1}{x=3, y=2, z=1}{z=1, y=2, x=3}
Show the answer
merge stores 1 the first time and adds 1 afterwards: x three times, y twice, z once. TreeMap prints keys sorted.
Walk entries, not keys
Looping over keySet() and calling get(key) does a second lookup per key. **entrySet() hands you key and value together. On mutable maps like HashMap and TreeMap, e.setValue(...)** writes straight back into the map.
for (var e : prices.entrySet()) {
e.setValue(e.getValue() * 2);
}Everyday Map code
Word counts, page-view counters, "orders grouped by customer", lazy caches: merge and computeIfAbsent show up constantly in production code and in coding interviews. They're shorter and harder to get wrong than hand-written get/null-check/put.
Key takeaways
- getOrDefault(k, d) returns d when k is missing — it doesn't insert
- computeIfAbsent(k, f) is perfect for 'map of lists' grouping
- merge(k, 1, Integer::sum) is the classic word counter
- entrySet() avoids a second lookup per key
getOrDefault, putIfAbsent, computeIfAbsent and merge were all added in Java 8 as default methods on the Map interface. That's how every existing Map implementation gained them at once.
Practice questions
What does this print?
Map<String, Integer> stock = new HashMap<>();
stock.put("apple", 3);
System.out.println(stock.getOrDefault("apple", 0));
System.out.println(stock.getOrDefault("kiwi", 0));
System.out.println(stock.size());- 3 0 2
- 3 0 1
- 3 null 1
- 3 0 0
Check your answer
3 0 1. getOrDefault returns the stored value or the fallback, but never adds anything — so the map still has one entry.
What does this print?
Map<String, Integer> counts = new TreeMap<>();
for (String w : "a b a c a b".split(" ")) {
counts.merge(w, 1, Integer::sum);
}
System.out.println(counts);- {a=1, b=1, c=1}
- {a=3, b=2, c=1}
- {c=1, b=2, a=3}
- {a=6, b=6, c=6}
Check your answer
{a=3, b=2, c=1}. merge puts 1 for a new key, or combines old + 1 with Integer::sum for an existing key. TreeMap prints the keys sorted.