Maps
A map stores key–value pairs: a word and its definition, a product code and its price, a user id and their profile. Looking up a value by its key is fast, keys are unique, and maps are behind countless everyday tasks — counting, grouping, caching and indexing.
HashMap basics
import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Double> prices = new HashMap<>(); prices.put("mug", 12.50); prices.put("lamp", 34.00); prices.put("mug", 11.90); System.out.println(prices.get("mug")); System.out.println(prices.get("sofa")); System.out.println(prices.getOrDefault("sofa", 0.0)); System.out.println(prices.containsKey("lamp") + " " + prices.size()); prices.remove("lamp"); System.out.println(prices); } }
11.9
null
0.0
true 2
{mug=11.9}put adds or replaces (keys are unique). get returns null for missing keys, so getOrDefault is often safer.
Iterating over a map
import java.util.Map; import java.util.TreeMap; public class Main { public static void main(String[] args) { Map<String, Integer> stock = new TreeMap<>(Map.of("mug", 200, "lamp", 15, "throw", 30)); for (Map.Entry<String, Integer> e : stock.entrySet()) { System.out.println(e.getKey() + ": " + e.getValue()); } System.out.println(stock.keySet()); System.out.println(stock.values()); stock.forEach((k, v) -> { if (v < 20) System.out.println("Low stock: " + k); }); } }
lamp: 15 mug: 200 throw: 30 [lamp, mug, throw] [15, 200, 30] Low stock: lamp
entrySet() gives each key with its value; keySet() and values() give one side. TreeMap keeps keys sorted, which is why this prints alphabetically.
Counting with merge
import java.util.Map; import java.util.TreeMap; public class Main { public static void main(String[] args) { String text = "to be or not to be that is the question"; Map<String, Integer> counts = new TreeMap<>(); for (String word : text.split(" ")) { counts.merge(word, 1, Integer::sum); } System.out.println(counts); } }
{be=2, is=1, not=1, or=1, question=1, that=1, the=1, to=2}merge(key, 1, Integer::sum) means "put 1 if the key is new, otherwise add 1 to the existing value" — the idiomatic way to count. (Integer::sum is a method reference, lesson 45.)
Grouping with computeIfAbsent
import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; public class Main { public static void main(String[] args) { String[][] people = {{"Ana", "Lisbon"}, {"Ben", "Manchester"}, {"Julia", "Lisbon"}, {"Isaac", "Porto"}}; Map<String, List<String>> byCity = new TreeMap<>(); for (String[] p : people) { byCity.computeIfAbsent(p[1], city -> new ArrayList<>()).add(p[0]); } System.out.println(byCity); } }
{Lisbon=[Ana, Julia], Manchester=[Ben], Porto=[Isaac]}computeIfAbsent creates the list the first time a city appears and returns the existing one afterwards — no if (!map.containsKey…) needed.
Choosing a map
| Class | Key order |
|---|---|
HashMap | None — the default choice |
LinkedHashMap | Insertion order (or access order, for LRU caches) |
TreeMap | Sorted, with range methods like firstKey, headMap, floorKey |
Map.of(…) | Unmodifiable, up to 10 pairs (Map.ofEntries for more) |
EnumMap | Enum keys, declaration order |
Keys must have consistent equals/hashCode (or compareTo for TreeMap) and shouldn't change while in the map — immutable keys like strings and records are ideal.
Other handy methods
import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> scores = new HashMap<>(Map.of("Ana", 80)); scores.putIfAbsent("Ana", 0); scores.putIfAbsent("Ben", 0); scores.computeIfPresent("Ana", (name, s) -> s + 5); scores.replaceAll((name, s) -> Math.min(s, 100)); System.out.println(scores.get("Ana") + " " + scores.get("Ben")); } }
85 0
Try it yourself
Build an index of the first letter of each word to the words starting with it.
a -> [apple, apricot, avocado] b -> [banana, blueberry] c -> [cherry]