Map / HashMap
java.util.Map is the key/value collection interface. HashMap is the workhorse (O(1) ops, no ordering), LinkedHashMap preserves insertion order, TreeMap maintains sorted order with O(log n), and ConcurrentHashMap is the thread-safe one. Pick by the access pattern, not by habit.
HashMap, LinkedHashMap, TreeMap, and modern helpers
EXAMPLE
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.*;
public class MapsDemo {
public static void main(String[] args) {
// 1) HashMap — the default
Map<String, Integer> scores = new HashMap<>();
scores.put("alice", 90);
scores.put("bob", 75);
// putIfAbsent vs merge vs compute — modern, atomic style
scores.putIfAbsent("carol", 0); // only set if missing
scores.merge("alice", 5, Integer::sum); // add 5; insert if absent
scores.compute("bob", (k, v) -> v == null ? 0 : v + 10);
// getOrDefault — replace the ternary
int e = scores.getOrDefault("eve", 0);
System.out.println(scores);
// 2) Iteration patterns
scores.forEach((k, v) -> System.out.println(k + " -> " + v));
for (Map.Entry<String, Integer> e1 : scores.entrySet()) {
// change only the value via setValue() — avoids re-put cost
if (e1.getValue() < 80) e1.setValue(80);
}
// 3) Immutable Map (Java 9+)
Map<String, Integer> caps = Map.of("AU", 26_000_000, "NZ", 5_000_000);
// caps.put(...); // throws UnsupportedOperationException
// 4) LinkedHashMap — insertion order preserved (great for LRU when accessOrder=true)
Map<String, Integer> ordered = new LinkedHashMap<>();
ordered.put("c", 3); ordered.put("a", 1); ordered.put("b", 2);
System.out.println(ordered); // {c=3, a=1, b=2}
Map<String, Integer> lru = new LinkedHashMap<>(16, 0.75f, true) {
@Override protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > 3; // bound the cache
}
};
// 5) TreeMap — sorted by key, O(log n) ops, range queries
TreeMap<String, Integer> sorted = new TreeMap<>(scores);
System.out.println(sorted.firstKey() + " ... " + sorted.lastKey());
System.out.println(sorted.headMap("c")); // keys < "c"
// 6) ConcurrentHashMap — safe for concurrent reads + writes
ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<>();
counts.compute("hits", (k, v) -> v == null ? 1 : v + 1);
// Bulk parallel scan — uses ForkJoinPool when the map is large
counts.forEachKey(1L << 12, k -> System.out.println(k));
// 7) Grouping + counting from a stream — replaces a manual put loop
List<String> words = List.of("ant", "bee", "ant", "cat", "bee", "ant");
Map<String, Long> freq = words.stream()
.collect(Collectors.groupingBy(w -> w, Collectors.counting()));
System.out.println(freq); // {ant=3, bee=2, cat=1}
// 8) Inversion: from { k -> v } to { v -> [k] }
Map<Integer, List<String>> byScore = scores.entrySet().stream()
.collect(Collectors.groupingBy(Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
// 9) Map iteration safety — DO NOT mutate the map while iterating
// Use Iterator.remove(), or collect keys-to-remove and remove after.
scores.entrySet().removeIf(en -> en.getValue() < 60);
}
}
Why it matters
Reach for merge / compute / computeIfAbsent over the get-test-put dance. They are atomic on ConcurrentHashMap, shorter than the if/else version on any map, and they remove the off-by-one bugs in "add 1 to a counter that might be missing" code.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
Map<String, Integer> ages = new HashMap<>();
ages.put("ada", 36);
ages.computeIfAbsent("bo", k -> 28);
for (var e : ages.entrySet()) System.out.println(e.getKey() + "=" + e.getValue());
Try it Yourself »
Discussion
Loading…