🗃️ Collections Framework · Intermediate

Map API essentials in Java

getOrDefault, putIfAbsent, computeIfAbsent, merge, entrySet iteration.

🧩 The mysteryCounting words used to take five lines of get, null-check and put. Modern Java does it in one: 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);  // 0
🔮 Predict it

Your 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());
  1. 0 2
  2. 0 1
  3. null 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 z
⚠️ The trap

Grouping 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=2
🔮 Predict it

Count 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);
  1. {x=1, y=1, z=1}
  2. {x=3, y=2, z=1}
  3. {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);
}
💼 In the real world

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

  1. getOrDefault(k, d) returns d when k is missing — it doesn't insert
  2. computeIfAbsent(k, f) is perfect for 'map of lists' grouping
  3. merge(k, 1, Integer::sum) is the classic word counter
  4. entrySet() avoids a second lookup per key
🤯 Did you know?

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());
  1. 3 0 2
  2. 3 0 1
  3. 3 null 1
  4. 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);
  1. {a=1, b=1, c=1}
  2. {a=3, b=2, c=1}
  3. {c=1, b=2, a=3}
  4. {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.

Next: queues and stacks. Why does Java's own Stack class tell you not to use it?