Java course menu
Java HashMap and TreeMap: Key-Value Pairs, Counting and Grouping
Learn Java maps: HashMap put, get, getOrDefault, containsKey and remove, iterating with entrySet and forEach, counting with merge, grouping with computeIfAbsent, TreeMap for sorted keys and range queries, LinkedHashMap for insertion order, and how hashing works.
On this page
A map stores key-value pairs: each unique key maps to one value, such as a username to an email, a product code to a price, or a word to how many times it appears. Java's main map implementations are HashMap (fast, no order), LinkedHashMap (keeps insertion order) and TreeMap (keeps keys sorted).
Basic operations#
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> stock = new HashMap<>();
stock.put("pens", 40);
stock.put("books", 12);
stock.put("pens", 35); // same key: replaces the value
System.out.println(stock.get("pens"));
System.out.println(stock.get("caps")); // missing key: null
System.out.println(stock.getOrDefault("caps", 0));
System.out.println(stock.containsKey("books") + " " + stock.size());
stock.remove("books");
System.out.println(stock.containsKey("books"));
}
}35
null
0
true 2
falseIterating over a map#
Use entrySet() to get each key and value together, or forEach with a lambda. A TreeMap iterates in key order, which makes the output predictable:
import java.util.Map;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
Map<String, Double> prices = new TreeMap<>();
prices.put("tea", 20.0);
prices.put("coffee", 35.0);
prices.put("juice", 50.0);
for (Map.Entry<String, Double> e : prices.entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue());
}
prices.forEach((item, price) -> System.out.println(item.toUpperCase() + ": " + price));
}
}coffee = 35.0
juice = 50.0
tea = 20.0
COFFEE: 35.0
JUICE: 50.0
TEA: 20.0A HashMap iterates in an unspecified order that can change as the map grows. If order matters, use TreeMap (sorted) or LinkedHashMap (insertion order).
Counting with merge#
Counting occurrences is the most common map task. merge(key, 1, Integer::sum) puts 1 for a new key, or adds 1 to the existing count:
import java.util.Map;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
String text = "the cat and the hat and the bat";
Map<String, Integer> counts = new TreeMap<>();
for (String word : text.split(" ")) {
counts.merge(word, 1, Integer::sum);
}
System.out.println(counts);
}
}{and=2, bat=1, cat=1, hat=1, the=3}The longer equivalent is counts.put(word, counts.getOrDefault(word, 0) + 1);.
Grouping with computeIfAbsent#
To build a map of lists (group items under a key), computeIfAbsent creates the list the first time a key appears:
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) {
List<String> words = List.of("apple", "avocado", "banana", "blueberry", "cherry", "apricot");
Map<Character, List<String>> byLetter = new TreeMap<>();
for (String w : words) {
byLetter.computeIfAbsent(w.charAt(0), k -> new ArrayList<>()).add(w);
}
System.out.println(byLetter);
}
}{a=[apple, avocado, apricot], b=[banana, blueberry], c=[cherry]}Streams offer the same in one expression with Collectors.groupingBy; see Streams.
TreeMap: sorted keys and range queries#
Because a TreeMap keeps keys sorted, it can answer "the nearest key" questions quickly:
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
TreeMap<Integer, String> gradeFrom = new TreeMap<>();
gradeFrom.put(0, "F");
gradeFrom.put(40, "D");
gradeFrom.put(55, "C");
gradeFrom.put(70, "B");
gradeFrom.put(85, "A");
for (int marks : new int[] {92, 70, 38}) {
System.out.println(marks + " -> " + gradeFrom.floorEntry(marks).getValue());
}
System.out.println(gradeFrom.firstKey() + " " + gradeFrom.lastKey());
System.out.println(gradeFrom.headMap(55)); // keys below 55
}
}92 -> A
70 -> B
38 -> F
0 85
{0=F, 40=D}floorKey/floorEntry give the greatest key ≤ a value; ceilingKey the smallest key ≥ it. headMap, tailMap and subMap return ranges.
How HashMap works#
A HashMap uses each key's hashCode() to choose a bucket, then equals() to find the key inside it. That gives O(1) average time for get and put. Two consequences:
- Keys must implement
equalsandhashCodeconsistently.String, wrapper classes, enums and records already do; your own classes must override both. - Never change a key object after putting it in a map; its hash would change and the entry would be lost. Immutable keys (strings, records) avoid this.
Common mistakes#
Practice#
Practice exercise
Given sales records {"North", 1200}, {"South", 800}, {"North", 450}, {"East", 990}, {"South", 300} (use a List<Map.Entry<String, Integer>> built with Map.entry), total the sales per region in a TreeMap, print it, and print the region with the highest total.
Show solution
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class RegionSales {
public static void main(String[] args) {
List<Map.Entry<String, Integer>> sales = List.of(
Map.entry("North", 1200), Map.entry("South", 800), Map.entry("North", 450),
Map.entry("East", 990), Map.entry("South", 300));
Map<String, Integer> totals = new TreeMap<>();
for (Map.Entry<String, Integer> s : sales) {
totals.merge(s.getKey(), s.getValue(), Integer::sum);
}
System.out.println(totals);
String best = null;
int bestTotal = Integer.MIN_VALUE;
for (Map.Entry<String, Integer> e : totals.entrySet()) {
if (e.getValue() > bestTotal) {
bestTotal = e.getValue();
best = e.getKey();
}
}
System.out.println("Best: " + best);
}
}{East=990, North=1650, South=1100}
Best: NorthKey takeaway#
Finished this lesson?