DevNotes
Support
Collection Framework · Chapter 17

TreeMap

Sorted maps on a Red-Black Tree — ordering by key, comparator equivalence, floor/ceiling navigation, range views and rotations.

◷ 14 min read§ 21 sections? 6 interview questions{ } 31 code examples

#Introduction

TreeMap is the primary Java Map implementation when you need sorted keys. The central idea:

TreeMap stores mappings in a balanced Red-Black Tree, ordered by key.

Unlike HashMap, where ordering isn't guaranteed, TreeMap continuously maintains its keys according to their natural ordering or a supplied Comparator.

#What is TreeMap?

Package:

Java
java.util.TreeMap
Map
SortedMap
NavigableMap
TreeMapRed-Black Tree → sorted keys
Hierarchy

Basic Example

Java
Map<Integer, String> map = new TreeMap<>();

map.put(30, "C");
map.put(10, "A");
map.put(20, "B");
Output
10 → A
20 → B
30 → C

The insertion order doesn't matter — the keys are maintained in sorted order.

TreeMap Is Sorted by Key

This is critical: TreeMap<K, V> sorts according to K (the key), not V (the value).

Java
TreeMap<Integer, String> map = new TreeMap<>();

map.put(30, "Apple");
map.put(10, "Zebra");
map.put(20, "Mango");
Output
10 → Zebra
20 → Mango
30 → Apple

The values aren't sorted.

Why Use TreeMap?

Use TreeMap when you need:

  • Sorted keys
  • Range queries
  • First/last key
  • Nearest key
  • Floor/ceiling operations
  • Navigational operations

For example: "Find the greatest key less than or equal to X." That's exactly the kind of problem TreeMap handles efficiently.

#Internal Data Structure

TreeMap uses a Red-Black Tree.

  • 20
    • 10
      • 5
      • 15
    • 30
      • 25
      • 35

The actual implementation stores entries as tree nodes. Conceptually each node contains key, value, left, right, parent, color.

What Is a Red-Black Tree?

A self-balancing Binary Search Tree with additional rules involving node colours (RED / BLACK). These rules ensure the tree doesn't become excessively skewed, so its height remains O(log n) — giving TreeMap predictable logarithmic operations.

Why Not a Normal Binary Search Tree?

Inserting 10, 20, 30, 40, 50 into a normal BST could produce a tree that behaves almost like a linked list — search becomes O(n):

10
20
30
40
50
Unbalanced BST

A Red-Black Tree rebalances itself, so the height stays approximately logarithmic:

  • 20
    • 10
    • 40
      • 30
      • 50
Rebalanced

Red-Black Tree Properties

The classic rules are:

  1. Every node is either RED or BLACK.
  2. The root is black.
  3. Null leaves are treated as black.
  4. A red node cannot have a red child (RED → RED is invalid).
  5. Every path from a node to its descendant null leaves contains the same number of black nodes.

These rules maintain balance.

#TreeMap put()

Java
map.put(key, value);
put()
Compare key
Move left or right
Find insertion position
Insert node
Rebalance tree
The comparison determines where the key belongs

Natural Ordering

If no Comparator is supplied, keys must generally implement Comparable.

Java
TreeMap<Integer, String> map = new TreeMap<>();

Integer implements Comparable<Integer>, so TreeMap knows 10 < 20 < 30.

#Comparable vs Comparator

This distinction is essential.

Comparable

Defines natural ordering inside the class:

Java
class Employee implements Comparable<Employee> {

    @Override
    public int compareTo(Employee other) {
        return Integer.compare(this.id, other.id);
    }
}
Java
TreeMap<Employee, String> map = new TreeMap<>();

Comparator

Defines ordering outside the class:

Java
TreeMap<Employee, String> map = new TreeMap<>(
        Comparator.comparingInt(Employee::getSalary)
);

Now the TreeMap sorts keys by salary.

Comparator Takes Priority

If you supply new TreeMap<>(comparator), the TreeMap uses that Comparator for key ordering rather than the key's natural ordering. This is useful when the same class needs different sorting requirements.

Java
class Employee {
    String name;
    int salary;
}

TreeMap<Employee, String> map = new TreeMap<>(Comparator.comparingInt(e -> e.salary));

Now TreeMap's ordering is determined by salary.

#Key Equivalence: compare() == 0

Critical Comparator Trap

TreeMap uses its ordering comparison to determine key equivalence.

Java
Comparator<Employee> comparator = Comparator.comparingInt(Employee::getSalary);

Two different Employees with the same salary compare as compare(a, b) == 0. TreeMap can therefore treat them as the same key for Map purposes, even if a.equals(b) == false. This is a major interview concept.

compareTo() == 0 and Key Uniqueness

For TreeMap, key uniqueness is effectively determined by compareTo() / Comparator, not simply by equals().

Employee A (salary 50000) and Employee B (salary 50000) with a salary-only comparator → compare(A, B) == 0 → treated as equivalent keys.

Comparator Consistency Trap

Java
Comparator<String> comparator = (a, b) -> Integer.compare(a.length(), b.length());

TreeMap<String, Integer> map = new TreeMap<>(comparator);

map.put("Java", 1);
map.put("Code", 2);

Both strings have length 4, so compare("Java", "Code") == 0. The second insertion replaces the first mapping's value — the map ends up with a single entry (key "Java", value 2). This is why Comparator design matters enormously with TreeMap.

#TreeMap get()

Java
map.get(key);

TreeMap starts at the root:

Root
compare()
Key smaller?
go left
go right
compare again
It continues until found or not found. Because the tree is balanced: O(log n)

#TreeMap Complexity

OperationComplexity
put()O(log n)
get()O(log n)
remove()O(log n)
containsKey()O(log n)
firstKey() / lastKey()O(log n)
lowerKey() / higherKey()O(log n)
floorKey() / ceilingKey()O(log n)

TreeMap trades HashMap's average O(1) lookup for a guaranteed sorted structure + navigational operations.

#Range Queries

One of TreeMap's biggest advantages. For keys 10, 20, 30, 40, 50, to get 20 through 40:

Java
map.subMap(20, true, 40, true);   // 20, 30, 40

subMap()

Modern form: subMap(fromKey, fromInclusive, toKey, toInclusive).

Java
map.subMap(20, true, 40, false);   // 20, 30 — 20 included, 40 excluded

headMap()

Entries before a specified key:

Java
map.headMap(30);         // 10, 20
map.headMap(30, true);   // 10, 20, 30

tailMap()

Entries from a specified key onward:

Java
map.tailMap(30);          // 30, 40, 50
map.tailMap(30, false);   // 40, 50

Range Views Are Views

This is important. subMap(), headMap() and tailMap() return views, not necessarily independent copies. Modifications can affect the original TreeMap. This is useful but requires care.

#Null Handling

Null Keys

A common interview trap. TreeMap generally does not permit a null key when using natural ordering:

Java
TreeMap<Integer, String> map = new TreeMap<>();

map.put(null, "A");
Output
NullPointerException

Why? Because TreeMap needs to compare keys.

Null Values

Null values are generally allowed — map.put(10, null); — the issue is the key comparison, not the value.

Can a Comparator Support Null Keys?

Potentially yes, if the supplied Comparator explicitly handles null:

Java
TreeMap<Integer, String> map = new TreeMap<>(
        Comparator.nullsFirst(Comparator.naturalOrder())
);

#Thread Safety

Like HashMap and LinkedHashMap, TreeMap is not inherently thread-safe. For concurrent sorted-map requirements, Java provides ConcurrentSkipListMap rather than a synchronized TreeMap implementation.

#Comparisons

TreeMap vs HashMap

FeatureHashMapTreeMap
Internal structureHash tableRed-Black Tree
OrderingNone guaranteedSorted keys
Average get()O(1)O(log n)
put()O(1) avgO(log n)
Range queriesNot naturalExcellent
Navigation❌ No✅ Yes
Null keyOne allowedGenerally no
Thread-safe❌ No❌ No

Use HashMap for the fastest general-purpose lookup; use TreeMap for sorted/navigable key operations.

TreeMap vs LinkedHashMap

FeatureLinkedHashMapTreeMap
OrderingInsertion/accessSorted
StructureHash table + linksRed-Black Tree
LookupO(1) avgO(log n)
Range operationsLimitedExcellent
Navigation❌ No✅ Yes
LRU use✅ Yes❌ No
Null keyOne allowedGenerally no

When TreeMap Is the Right Choice

Strong TreeMap signals in a requirement:

  • "Keep keys sorted."
  • "Find the nearest key."
  • "Find all entries between X and Y."
  • "Find the smallest key greater than X."

#Real-World Examples

Price ranges

Price → Product: find the highest price not exceeding 5000.

Java
Integer price = products.floorKey(5000);

Much cleaner than scanning every entry.

Scheduling

timestamp → task: find the next scheduled task after time T.

Java
map.higherEntry(timestamp);

Range query

Employee ID → Employee: employees with IDs between 1000 and 2000.

Java
map.subMap(1000, true, 2000, true);

#Mutable Keys and Rebalancing

Mutable Keys and TreeMap

HashMap has a mutable-key problem involving hashCode(). TreeMap has a related problem involving ordering. If the fields used by the Comparator/compareTo() change after insertion, the tree's ordering assumptions can become invalid.

Employee salary = 50000
insert into TreeMap
salary changes to 90000
node is now in the wrong place

put() Mental Model

put(key, value)
Compare with root
Smaller or larger?
go left
go right
repeat comparison
insert node
rebalance
The Red-Black Tree maintains balance after insertion

remove() Mental Model

remove(key)
Search tree
Find node
Remove node
Rebalance
Tree remains balanced
O(log n)

Tree Rotations

Rebalancing can use left rotations and right rotations. A right rotation on a left-leaning chain:

30 → 20 → 10left-leaning chain
20children: 10, 30
Rotations change structure while preserving sorted-order relationships

Why Rotation Works

The in-order ordering remains 10 < 20 < 30, but the tree becomes much more balanced. Red-Black Tree rules determine when rotations/recolouring are necessary.

#Keys, Values and Entries

Does TreeMap Store Sorted Values?

No. It stores sorted keys. The values remain associated with their keys but don't influence tree ordering (unless the values are actually part of the key/comparator design).

Can TreeMap Have Duplicate Values?

Yes — map.put(10, "A"); map.put(20, "A"); is perfectly valid.

Can TreeMap Have Duplicate Keys?

No. But remember: "duplicate" is determined by the TreeMap's comparison. If compare(k1, k2) == 0, TreeMap treats them as equivalent keys.

firstKey() vs firstEntry()

firstKey() returns K, while firstEntry() returns Map.Entry<K,V>. Likewise lastKey() / lastEntry().

pollFirstEntry() vs firstEntry()

MethodBehaviour
firstEntry() / lastEntry()Inspect only
pollFirstEntry() / pollLastEntry()Inspect + remove

#Interview Scenarios

RequirementUse
Student ID → Student in sorted ID orderTreeMap<Integer, Student>
Nearest key ≤ Xmap.floorKey(x)
Nearest key ≥ Xmap.ceilingKey(x)
All mappings in a rangemap.subMap(from, true, to, true)
Next key after Xmap.higherKey(x)
Previous key before Xmap.lowerKey(x)

#Coding and Tricky Questions

Q1firstKey() and lastKey()
Java
TreeMap<Integer, String> map = new TreeMap<>();

map.put(30, "C");
map.put(10, "A");
map.put(20, "B");

System.out.println(map.firstKey());
System.out.println(map.lastKey());
Output
10
30
Q2floorKey() and ceilingKey()

Given keys 10, 20, 30, 40: map.floorKey(25) → 20, map.ceilingKey(25) → 30.

Q3lowerKey() and higherKey()

Given keys 10, 20, 30, 40: map.lowerKey(30) → 20, map.higherKey(30) → 40.

Remember: lower → <, higher → >, floor → <=, ceiling → >=.

Q4Does TreeMap use equals() to determine whether two keys are the same?

Not directly. TreeMap relies on its ordering — compareTo() or Comparator.compare(). If comparison returns zero, the keys are considered equivalent for Map operations.

Q5Why can two objects that are not equal according to equals() behave as the same TreeMap key?

Because the Comparator may return compare(a, b) == 0, and TreeMap uses that ordering comparison to identify equivalent keys.

Q6Why is TreeMap slower than HashMap for simple lookup?

HashMap: hash calculation → bucket → O(1) average. TreeMap: tree traversal → O(log n). But TreeMap provides capabilities HashMap doesn't: sorting + range queries + navigation.

#Decision Framework

NeedChoose
Sorted keysTreeMap
floor / ceiling / lower / higher / rangeTreeMap
Only fast average lookupHashMap
Insertion/access orderLinkedHashMap
Concurrent sorted mapConcurrentSkipListMap

Final Mental Model

  • TreeMap
    • Red-Black Tree
      • Left subtreesmaller keys
      • Right subtreelarger keys

Every key follows: Key → Comparator / compareTo() → left or right → balanced tree.

The main trade-off:

MapLookupCapability
HashMapO(1) averageNo ordering
TreeMapO(log n)Sorted + navigable

#Chapter Summary

ConceptKey Point
TreeMapSorted Map
Internal structureRed-Black Tree
OrderingKey ordering
Natural orderingComparable
Custom orderingComparator
put() / get() / remove()O(log n)
firstKey()Smallest key
lastKey()Largest key
lowerKey()< key
floorKey()<= key
ceilingKey()>= key
higherKey()> key
subMap()Range view
headMap()Keys before boundary
tailMap()Keys from boundary
descendingMap()Reverse-order view
Null keyGenerally not with natural ordering
Null valuesAllowed
Duplicate keysNo
Duplicate valuesYes
Thread-safeNo
Concurrent sorted alternativeConcurrentSkipListMap

Interview Readiness Checklist

  • What TreeMap is and why it uses a Red-Black Tree
  • Why a Red-Black Tree is preferred over an unbalanced BST
  • Red-Black Tree balancing concept
  • TreeMap put(), get() and remove() flows
  • Comparable vs Comparator
  • Why Comparator consistency with equals matters
  • Why compare() == 0 can mean equivalent TreeMap keys
  • firstKey() / lastKey(), lowerKey() / floorKey(), ceilingKey() / higherKey()
  • subMap() / headMap() / tailMap() and why range results are views
  • descendingMap()
  • TreeMap vs HashMap / LinkedHashMap
  • Null-key behaviour
  • Mutable ordering-key problem
  • Why TreeMap isn't thread-safe
  • When to choose TreeMap