Skip to content
Laviyx Learn

Try

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.

Intermediate11 min readUpdated
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#

Java
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"));
    }
}
Output
35
null
0
true 2
false
MethodMeaning
put(k, v)Add or replace; returns the old value (or null)
get(k)The value, or null if the key is missing
getOrDefault(k, d)The value, or d if missing
containsKey(k) / containsValue(v)Membership tests
remove(k)Remove a key
putIfAbsent(k, v)Add only if the key is missing
keySet(), values(), entrySet()Views of keys, values and pairs

Iterating 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:

Java
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));
    }
}
Output
coffee = 35.0
juice = 50.0
tea = 20.0
COFFEE: 35.0
JUICE: 50.0
TEA: 20.0

A 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:

Java
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);
    }
}
Output
{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:

Java
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);
    }
}
Output
{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:

Java
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
    }
}
Output
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 equals and hashCode consistently. 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.
HashMapLinkedHashMapTreeMap
OrderNoneInsertion orderSorted by key
get / putO(1) averageO(1) averageO(log n)
null keysOne allowedOne allowedNot allowed

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
Java
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);
    }
}
Output
{East=990, North=1650, South=1100}
Best: North

Key takeaway#

Finished this lesson?