Skip to content
intermediatePhase 13 · Java Collections

TreeMap & LinkedHashMap

Use TreeMap for sorted keys and LinkedHashMap for insertion order.

45m
2 problems
Topic Progress0%

TreeMap

TreeMap

TreeMap is a SortedMap implementation backed by a red-black tree (a self-balancing binary search tree). It keeps keys in sorted order according to their natural ordering or a custom Comparator.

Key characteristics:

  • Keys are sorted (ascending order by default)
  • O(log n) for get, put, remove
  • No null keys (throws NullPointerException)
  • Based on red-black tree: guaranteed O(log n)
import java.util.*;

public class TreeMapDemo {
    public static void main(String[] args) {
        // Creating TreeMap - keys sorted by natural order
        TreeMap<String, Integer> map = new TreeMap<>();
        map.put("Banana", 3);
        map.put("Apple", 5);
        map.put("Cherry", 2);
        map.put("Date", 8);
        map.put("Elderberry", 1);

        System.out.println("Sorted map: " + map);
        // {Apple=5, Banana=3, Cherry=2, Date=8, Elderberry=1}

        // Navigation methods
        System.out.println("First key: " + map.firstKey()); // Apple
        System.out.println("Last key: " + map.lastKey()); // Elderberry
        System.out.println("First entry: " + map.firstEntry()); // Apple=5
        System.out.println("Last entry: " + map.lastEntry()); // Elderberry=1

        // floorKey - greatest key <= given key
        System.out.println("floorKey(D): " + map.floorKey("D")); // Date
        System.out.println("floorKey(Ac): " + map.floorKey("Ac")); // Apple

        // ceilingKey - smallest key >= given key
        System.out.println("ceilingKey(D): " + map.ceilingKey("D")); // Date
        System.out.println("ceilingKey(Db): " + map.ceilingKey("Db")); // Elderberry

        // lowerKey - greatest key < given key (strictly less)
        System.out.println("lowerKey(Cherry): " + map.lowerKey("Cherry")); // Banana

        // higherKey - smallest key > given key (strictly greater)
        System.out.println("higherKey(Cherry): " + map.higherKey("Cherry")); // Date

        // SubMap, headMap, tailMap
        System.out.println("headMap(Cherry): " + map.headMap("Cherry")); // {Apple, Banana}
        System.out.println("tailMap(Cherry): " + map.tailMap("Cherry")); // {Cherry, Date, Elderberry}
        System.out.println("subMap(Banana, Date): " + map.subMap("Banana", "Date")); // {Banana, Cherry}

        // descendingMap
        System.out.println("Descending: " + map.descendingMap());
        // {Elderberry=1, Date=8, Cherry=2, Banana=3, Apple=5}

        // pollFirstEntry / pollLastEntry - remove and return
        Map.Entry<String, Integer> first = map.pollFirstEntry();
        System.out.println("Removed first: " + first); // Apple=5
    }
}

When to use TreeMap:

  • You need keys in sorted order
  • You need navigation methods (floor, ceiling, higher, lower)
  • You need range views (subMap, headMap, tailMap)
  • You need the smallest or largest key efficiently

LinkedHashMap

LinkedHashMap

LinkedHashMap extends HashMap by maintaining a doubly linked list across all entries. This preserves either insertion order or access order (LRU order).

Two modes:

  • Insertion order (default): entries iteration follows insertion order
  • Access order: entries iteration follows last access order (useful for LRU caches)
import java.util.*;

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        // Insertion order (default)
        LinkedHashMap<String, Integer> insertion = new LinkedHashMap<>();
        insertion.put("Banana", 3);
        insertion.put("Apple", 5);
        insertion.put("Cherry", 2);
        insertion.put("Date", 8);

        System.out.println("Insertion order: " + insertion);
        // {Banana=3, Apple=5, Cherry=2, Date=8} - same as insertion order

        // Access order - for LRU cache
        LinkedHashMap<String, Integer> access = new LinkedHashMap<>(16, 0.75f, true);
        access.put("A", 1);
        access.put("B", 2);
        access.put("C", 3);

        System.out.println("Before access: " + access); // {A=1, B=2, C=3}
        access.get("A"); // access A
        System.out.println("After accessing A: " + access); // {B=2, C=3, A=1}

        // LRU Cache implementation
        LruCache<String, String> cache = new LruCache<>(3);
        cache.put("key1", "value1");
        cache.put("key2", "value2");
        cache.put("key3", "value3");
        cache.get("key1"); // access key1
        cache.put("key4", "value4"); // evicts key2 (least recently used)
        System.out.println("LRU Cache: " + cache);

        // Override removeEldestEntry for automatic eviction
        LinkedHashMap<String, Integer> bounded = new LinkedHashMap<>(16, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
                return size() > 3;
            }
        };
        bounded.put("a", 1);
        bounded.put("b", 2);
        bounded.put("c", 3);
        bounded.put("d", 4); // evicts "a"
        System.out.println("Bounded map: " + bounded); // {b=2, c=3, d=4}
    }

    // Simple LRU Cache using LinkedHashMap
    static class LruCache<K, V> extends LinkedHashMap<K, V> {
        private final int maxSize;

        public LruCache(int maxSize) {
            super(16, 0.75f, true); // access order
            this.maxSize = maxSize;
        }

        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > maxSize;
        }
    }
}

When to use LinkedHashMap:

  • You need insertion order preserved
  • You need an LRU cache (access order + removeEldestEntry)
  • You need predictable iteration order without sorting

Methods

TreeMap Navigation Methods

TreeMap's NavigableMap interface provides powerful navigation methods.

import java.util.*;

public class TreeMapMethodsDemo {
    public static void main(String[] args) {
        TreeMap<Integer, String> map = new TreeMap<>();
        map.put(10, "Ten");
        map.put(20, "Twenty");
        map.put(30, "Thirty");
        map.put(40, "Forty");
        map.put(50, "Fifty");

        // Exact key search
        System.out.println("get(30): " + map.get(30)); // Thirty
        System.out.println("containsKey(20): " + map.containsValue("Twenty")); // true

        // Nearest key navigation
        System.out.println("\n--- Navigation ---");
        System.out.println("floorEntry(25): " + map.floorEntry(25)); // 20=Twenty
        System.out.println("ceilingEntry(25): " + map.ceilingEntry(25)); // 30=Thirty
        System.out.println("lowerEntry(30): " + map.lowerEntry(30)); // 20=Twenty
        System.out.println("higherEntry(30): " + map.higherEntry(30)); // 40=Forty

        // Floor vs lower: floor <=, lower <
        System.out.println("\nfloorKey(30): " + map.floorKey(30)); // 30 (includes equal)
        System.out.println("lowerKey(30): " + map.lowerKey(30)); // 20 (excludes equal)

        // Ceiling vs higher: ceiling >=, higher >
        System.out.println("ceilingKey(30): " + map.ceilingKey(30)); // 30 (includes equal)
        System.out.println("higherKey(30): " + map.higherKey(30)); // 40 (excludes equal)

        // First and last
        System.out.println("\nFirst: " + map.firstKey() + " = " + map.firstEntry().getValue());
        System.out.println("Last: " + map.lastKey() + " = " + map.lastEntry().getValue());

        // Range views
        System.out.println("\n--- Range Views ---");
        System.out.println("headMap(30): " + map.headMap(30)); // {10, 20}
        System.out.println("tailMap(30): " + map.tailMap(30)); // {30, 40, 50}
        System.out.println("subMap(20, 40): " + map.subMap(20, 40)); // {20, 30}
        System.out.println("subMap(20, true, 40, false): " + map.subMap(20, true, 40, false));

        // Descending
        System.out.println("\nDescending: " + map.descendingMap());
        System.out.println("Descending key set: " + map.descendingKeySet());

        // Poll entries
        System.out.println("pollFirstEntry: " + map.pollFirstEntry()); // 10=Ten
        System.out.println("pollLastEntry: " + map.pollLastEntry()); // 50=Fifty
        System.out.println("After polls: " + map);
    }
}

Comparison of navigation methods:

Method Returns
floorKey(k) greatest key <= k, or null
ceilingKey(k) smallest key >= k, or null
lowerKey(k) greatest key < k, or null
higherKey(k) smallest key > k, or null

When to Use

When to Use Each Map Implementation

Map Ordering Performance Null Keys Use Case
HashMap None O(1) avg One null key General purpose, fast lookup
LinkedHashMap Insertion/Access order O(1) avg One null key LRU cache, predictable iteration
TreeMap Sorted by key O(log n) No null keys Sorted iteration, navigation, ranges
import java.util.*;

public class MapComparisonDemo {
    public static void main(String[] args) {
        // Use HashMap when: fast lookup, no ordering needed
        Map<String, Integer> cache = new HashMap<>();
        cache.put("user:1", 100);
        cache.put("user:2", 200);
        int user1 = cache.getOrDefault("user:1", 0); // O(1)

        // Use LinkedHashMap when: insertion order matters
        LinkedHashMap<String, Integer> config = new LinkedHashMap<>();
        config.put("database.url", 1);
        config.put("database.user", 2);
        config.put("database.pass", 3);
        for (Map.Entry<String, Integer> entry : config.entrySet()) {
            System.out.println("Config: " + entry.getKey() + " = " + entry.getValue());
        }

        // Use TreeMap when: sorted keys needed
        TreeMap<String, Integer> sortedScores = new TreeMap<>();
        sortedScores.put("Alice", 95);
        sortedScores.put("Bob", 87);
        sortedScores.put("Charlie", 92);
        System.out.println("Sorted scores: " + sortedScores);
        System.out.println("Top score: " + sortedScores.lastEntry());
        System.out.println("Bottom score: " + sortedScores.firstEntry());

        // Use LinkedHashMap with access order for LRU
        LinkedHashMap<String, String> lru = new LinkedHashMap<>(16, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
                return size() > 3;
            }
        };
        lru.put("a", "1");
        lru.put("b", "2");
        lru.put("c", "3");
        lru.get("a"); // access "a"
        lru.put("d", "4"); // evicts "b"
        System.out.println("LRU: " + lru);

        // Use TreeMap for range queries
        TreeMap<Integer, String> events = new TreeMap<>();
        events.put(2020, "Pandemic");
        events.put(2021, "Recovery");
        events.put(2022, "Growth");
        events.put(2023, "Innovation");
        events.put(2024, "AI Boom");
        System.out.println("Events 2021-2023: " + events.subMap(2021, 2024));
        System.out.println("Most recent: " + events.lastEntry());
    }
}

Decision framework:

  • Need O(1) lookup with no ordering? → HashMap
  • Need predictable iteration order? → LinkedHashMap
  • Need sorted keys or navigation? → TreeMap
  • Need LRU cache? → LinkedHashMap (access order + removeEldestEntry)

Practice Problems

0/2solved
Find Kth Smallest Element

Given a TreeSet of integers and an integer k, return the kth smallest element. Use TreeMap's navigation methods.

Solution
import java.util.*;

public class KthSmallest {
    public static int kthSmallest(TreeSet<Integer> set, int k) {
        if (k <= 0 || k > set.size()) throw new IllegalArgumentException("Invalid k");
        Iterator<Integer> it = set.iterator();
        for (int i = 1; i < k; i++) {
            it.next();
        }
        return it.next();
    }
}
LRU Cache

Implement an LRU (Least Recently Used) cache using LinkedHashMap with access order. It should have get(key) and put(key, value) methods with O(1) time complexity.

Solution
import java.util.*;

public class LRUCache {
    private int capacity;
    private LinkedHashMap<Integer, Integer> map;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.map = new LinkedHashMap<>(16, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
                return size() > LRUCache.this.capacity;
            }
        };
    }

    public int get(int key) {
        return map.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        map.put(key, value);
    }
}

Quiz

1. What data structure does TreeMap use internally?

Question 1 options

2. What is the difference between floorKey() and lowerKey()?

Question 2 options

3. How do you create a LinkedHashMap that maintains access order for an LRU cache?

Question 3 options

4. What is the primary purpose of Java TreeMap and LinkedHashMap?

Question 4 options

Flashcards

Question

What is TreeMap and what data structure does it use?

Answer

TreeMap is a SortedMap backed by a red-black tree. It keeps keys sorted and provides O(log n) for get, put, and remove. It also supports navigation methods like floorKey, ceilingKey, higherKey, lowerKey.

Question

What is LinkedHashMap and what are its two ordering modes?

Answer

LinkedHashMap extends HashMap with a doubly linked list. It supports insertion order (default) and access order (accessOrder=true). Access order mode is used for LRU caches.

Question

When would you use TreeMap over HashMap?

Answer

When you need keys in sorted order, navigation methods (floor, ceiling), range views (subMap, headMap, tailMap), or the smallest/largest key. TreeMap costs O(log n) vs HashMap O(1).

Question

What is Java TreeMap and LinkedHashMap?

Answer

Java TreeMap and LinkedHashMap is a key concept in Java programming.

Question

When to use Java TreeMap and LinkedHashMap?

Answer

Use Java TreeMap and LinkedHashMap when building production systems that require reliability, scalability, and maintainability.

Revision Notes

Key Takeaways

  • 1.TreeMap keeps keys sorted using a red-black tree (O(log n))
  • 2.LinkedHashMap preserves insertion or access order (O(1))
  • 3.Use LinkedHashMap with access order for LRU caches
  • 4.TreeMap's navigation methods find nearest keys efficiently

Interview Tips

  • Explain TreeMap's O(log n) guarantee vs HashMap's O(1) average
  • Demonstrate how to implement an LRU cache with LinkedHashMap
  • Know the difference between floorKey/ceilingKey and lowerKey/higherKey
  • Discuss when sorted order justifies the O(log n) cost

Cheat Sheet

TreeMap & LinkedHashMap Cheat Sheet

TreeMap

  • SortedMap backed by red-black tree
  • O(log n) for get/put/remove
  • No null keys
  • Navigation: floorKey, ceilingKey, higherKey, lowerKey
  • Range: headMap, tailMap, subMap
  • First/Last: firstKey, lastKey, firstEntry, lastEntry

LinkedHashMap

  • HashMap + doubly linked list
  • Insertion order (default) or access order
  • O(1) average for get/put/remove
  • LRU cache: accessOrder=true + removeEldestEntry()

Decision

HashMap: O(1), no ordering
LinkedHashMap: O(1), insertion/access order
TreeMap: O(log n), sorted keys, navigation