Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

Map Interface: HashMap, LinkedHashMap, TreeMap, Hashtable

A Map associates unique keys with values — it is not a Collection, because it stores pairs rather than single elements, but it is just as central to everyday Java code. Java provides four commonly used implementations, differing in ordering, null handling, and thread safety.

Why it matters

Maps are how Java programs answer "given this key, what is the value?" without scanning a list: a user by id, a price by product code, a count by word. Picking the wrong implementation shows up as real bugs — output that changes order between runs, a NullPointerException on a null key, or a lookup that slows down as data grows — so knowing the four trade-offs is worth more than memorising their method names.

key "BB" hashCode() = 2112 buckets 0 1 2 3 ... Aa=10 BB=20
How HashMap finds a bucket: the key's hash code picks an index, and keys that share a bucket are told apart with equals().

HashMap — The Default Choice

HashMap stores key-value pairs in a hash table keyed by the key's hash code, giving average O(1) get(), put(), and remove(). Iteration order is unspecified and can change. HashMap allows exactly one null key and any number of null values. Internally, it maintains a load factor (default 0.75) — once the table is 75% full, it resizes (roughly doubles) and rehashes all entries, which is an O(n) operation that happens infrequently enough not to affect the amortized O(1) average.

LinkedHashMap — Predictable Iteration Order

LinkedHashMap extends HashMap and additionally threads a doubly linked list through the entries, preserving insertion order by default (or, with a constructor flag, access order — useful for building an LRU cache). It has the same O(1) average performance as HashMap with a small constant overhead, and it also permits one null key.

TreeMap — Sorted by Key

TreeMap is backed by a red-black tree and keeps keys in sorted order (natural order, or a supplied Comparator). get(), put(), and remove() run in O(log n) time. TreeMap does not allow a null key (it throws NullPointerException, since it needs to compare keys to place them), though it does allow null values. It also offers navigation methods like firstKey(), ceilingKey(), and headMap().

Hashtable — Legacy, Synchronized

Hashtable is a pre-Collections-Framework class (like Vector) that was retrofitted to implement Map. Every method is synchronized, making it thread-safe but slower than HashMap for single-threaded use. Unlike HashMap, Hashtable permits no null keys and no null values — attempting either throws NullPointerException immediately. Modern concurrent code prefers ConcurrentHashMap instead of Hashtable.

  • HashMap: no order, O(1) average, one null key allowed, many null values allowed, not synchronized.
  • LinkedHashMap: insertion (or access) order, O(1) average, one null key allowed.
  • TreeMap: sorted by key, O(log n), no null keys, null values allowed.
  • Hashtable: legacy, synchronized, no null keys or values allowed at all.

Time complexity

OperationAverageWorst case
HashMap / LinkedHashMap — get, put, removeO(1)O(log n) since Java 8, when a crowded bucket becomes a tree (O(n) before)
TreeMap — get, put, removeO(log n)O(log n)
Hashtable — get, put, removeO(1)O(n)
HashMap resize (rehash all entries)O(n), amortised away over many putsO(n)

Examples

Null handling and ordering differences across Map implementations

Java
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class MapDemo {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("apple", 3);
        hashMap.put(null, 99); // allowed: one null key permitted
        System.out.println("HashMap null key value: " + hashMap.get(null));

        Map<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("banana", 5);
        treeMap.put("apple", 3);
        treeMap.put("cherry", 8);
        System.out.println("TreeMap sorted: " + treeMap);

        try {
            treeMap.put(null, 1); // TreeMap cannot place a null key
        } catch (NullPointerException e) {
            System.out.println("TreeMap rejected null key");
        }
    }
}
Output
HashMap null key value: 99
TreeMap sorted: {apple=3, banana=5, cherry=8}
TreeMap rejected null key

Two different keys with the same hash code (a collision)

Java
import java.util.HashMap;
import java.util.Map;

public class CollisionDemo {
    public static void main(String[] args) {
        // "Aa" and "BB" are different strings with the same hash code
        System.out.println("Aa".hashCode());
        System.out.println("BB".hashCode());

        Map<String, Integer> stock = new HashMap<>();
        stock.put("Aa", 10);
        stock.put("BB", 20);

        // Both land in the same bucket; equals() tells them apart
        System.out.println(stock.get("Aa"));
        System.out.println(stock.get("BB"));
        System.out.println(stock.size());
    }
}
Output
2112
2112
10
20
2

Where this is used

Counting and grouping are the most common everyday uses of a map: page views per URL, orders per customer, errors per type. merge() does the "insert 1 or add 1" step in a single call, so there is no separate check for whether the key already exists.

Counting page views per URL

Java
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PageViews {
    public static void main(String[] args) {
        List<String> requests = List.of("/home", "/pricing", "/home", "/blog", "/home");

        Map<String, Integer> views = new HashMap<>();
        for (String path : requests) {
            views.merge(path, 1, Integer::sum);
        }

        System.out.println("/home: " + views.get("/home"));
        System.out.println("/pricing: " + views.get("/pricing"));
        System.out.println("/missing: " + views.getOrDefault("/missing", 0));
    }
}
Output
/home: 3
/pricing: 1
/missing: 0

Common Mistakes

  • Assuming HashMap iteration order is insertion order — that guarantee belongs to LinkedHashMap, not HashMap.
  • Putting a null key into a TreeMap or Hashtable and being surprised by a NullPointerException.
  • Choosing Hashtable for thread safety in new code instead of the far more scalable ConcurrentHashMap.
  • Overriding equals() on a key class without overriding hashCode() to match, which breaks lookups in any hash-based Map.

Exercise

Count how many times each word appears in a sentence and print the result in alphabetical order. Start from the code below and replace the TODO.

The expected output is {be=2, not=1, or=1, to=2}. Once it works, change TreeMap to HashMap and explain why the printed order is no longer guaranteed.

Java · starter code
import java.util.Map;
import java.util.TreeMap;

public class WordCount {
    public static void main(String[] args) {
        String text = "to be or not to be";
        Map<String, Integer> counts = new TreeMap<>();

        // TODO: split text on spaces and count each word

        System.out.println(counts);
    }
}
Show hints
  • text.split(" ") gives you an array of words to loop over.
  • counts.merge(word, 1, Integer::sum) inserts 1 for a new word or adds 1 to an existing count.
Show solution
Java · solution
import java.util.Map;
import java.util.TreeMap;

public class WordCount {
    public static void main(String[] args) {
        String text = "to be or not to be";
        Map<String, Integer> counts = new TreeMap<>();

        for (String word : text.split(" ")) {
            counts.merge(word, 1, Integer::sum);
        }

        System.out.println(counts); // {be=2, not=1, or=1, to=2}
    }
}

Quick quiz

1. You need to print report rows sorted by customer name. Which map keeps its keys sorted?

  • A. HashMap
  • B. LinkedHashMap
  • C. TreeMap
  • D. Hashtable
Show answer

C. TreeMap keeps keys in sorted order. LinkedHashMap keeps insertion order, and HashMap and Hashtable promise no order.

2. What happens when you call put(null, 1) on a TreeMap that uses natural ordering?

  • A. It stores the entry under a null key
  • B. It throws NullPointerException
  • C. It silently ignores the call
  • D. It replaces the first entry
Show answer

B. TreeMap must compare the new key with existing keys to place it, and null cannot be compared, so it throws NullPointerException.

3. A HashMap is created with the default capacity (16) and load factor (0.75). When does it first resize?

  • A. When the 9th entry is added
  • B. When the 13th entry is added
  • C. When the 16th entry is added
  • D. Only when you call a resize method
Show answer

B. The threshold is 16 × 0.75 = 12 entries. Adding the 13th entry exceeds it, so the table doubles to 32 buckets and entries are redistributed.

Interview questions

What is the difference between HashMap and Hashtable?

Show answer

HashMap is not synchronized and allows one null key and any number of null values. Hashtable is a legacy class whose methods are all synchronized, and it rejects null keys and null values with a NullPointerException.

For thread-safe code today, the usual answer is ConcurrentHashMap rather than Hashtable, because it does not lock the whole table for every operation.

Why must a class used as a HashMap key override both equals() and hashCode()?

Show answer

HashMap uses hashCode() to choose the bucket and equals() to find the exact key inside that bucket. If two keys are equal but return different hash codes, they land in different buckets, so a lookup with an equal key finds nothing and the map can hold duplicates.

The rule is: equal objects must have equal hash codes. Keys should also be immutable, because changing a field that feeds hashCode() after insertion leaves the entry in the wrong bucket.

How would you build a simple LRU cache using the collections framework?

Show answer

Extend LinkedHashMap, pass true for the accessOrder constructor argument so that each get() moves the entry to the end, and override removeEldestEntry() to return true when size() exceeds the cache limit. The least recently used entry is then evicted automatically on insert.

This version is not thread-safe; it needs external synchronization or a different design for concurrent use.

Key Points to Remember

  • HashMap: no order guarantee, O(1) average, one null key allowed.
  • LinkedHashMap: insertion (or access) order preserved, same performance as HashMap.
  • TreeMap: sorted by key, O(log n), no null keys allowed.
  • Hashtable: legacy and synchronized, disallows both null keys and null values entirely.

Go deeper

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering