1. Introduction to TreeMap
TreeMap<K, V> is an implementation of Java's Map interface that keeps its entries sorted by key. It is useful when an application needs more than key-based lookup: it may need to visit entries in order, find the nearest key to a given value, or retrieve a range of entries without sorting the entire map repeatedly.
Typical examples include:
- Displaying orders from the earliest date to the latest.
- Finding the highest or lowest score on a leaderboard.
- Finding the price tier at or just below a cart total.
- Retrieving orders whose timestamps fall within a particular interval.
Unlike HashMap, which does not guarantee iteration order, and LinkedHashMap, which maintains insertion or access order, TreeMap maintains key order. The trade-off is performance: its basic lookup and update operations are O(log n), whereas HashMap usually provides expected O(1) key lookup when hashes are well distributed.
2. How TreeMap Maintains Sorted Keys
TreeMap uses a red-black tree, a self-balancing binary search tree. Each node stores a key-value mapping and links to child nodes. The binary-search-tree property means that smaller keys belong on the left side and larger keys belong on the right side, according to the map's ordering rule.
A normal binary search tree can become badly unbalanced if keys arrive in an unfortunate order. A red-black tree maintains balancing properties so its height stays proportional to the logarithm of the number of entries. This is why TreeMap can offer logarithmic time for its main key-based operations.
The balancing rules themselves are implementation details. For everyday use, the key facts are that the tree stays balanced, entries are sorted by key, and core operations have logarithmic complexity.
3. TreeMap and the Map Interface
TreeMap implements Map and also supports the sorted-map and navigable-map capabilities. The ordinary Map methods work as expected:
| Method | Purpose |
|---|---|
put(key, value) | Insert a mapping or replace a value |
get(key) | Retrieve the value associated with a key |
remove(key) | Remove a mapping |
containsKey(key) | Check whether a key exists |
size() | Count mappings |
firstKey() / lastKey() | Get the smallest or largest key |
The important difference is that iteration through the map's key, value, and entry views follows sorted key order. The order comes from the keys' natural ordering or from a comparator supplied when the map is constructed.
4. Natural Ordering and Custom Comparators
By default, TreeMap uses the natural ordering of its keys. For common types, this means:
Integer,Long, andDoubleuse numeric order.LocalDateuses chronological order.Stringuses lexicographic order.
ISO-formatted date strings such as 2026-05-10 sort chronologically under lexicographic ordering because the year, month, and day are arranged from most significant to least significant and consistently zero-padded.
If the desired order differs from natural ordering, pass a Comparator to the constructor. A comparator can define descending date order, case-insensitive string order, or ordering by a field of a custom class. The comparator must be consistent enough for the application to treat keys predictably; the map's ordering rule determines where keys are placed and how key equivalence is determined for map operations.
5. Basic Operations and Sorted Iteration
The core methods—put(), get(), remove(), containsKey(), and size()—work in the same general way as other map implementations. The visible difference appears during iteration.
Suppose date entries are inserted in this order: May 12, May 8, May 11, and May 9. Iterating over a TreeMap visits the dates in ascending key order rather than the order in which they were inserted.
This makes TreeMap useful for data that is naturally ordered. It also removes the need to copy keys to a separate list and sort them each time the application needs ordered output.
6. Endpoint Lookups: First and Last
The endpoint methods return the smallest or largest key, or the complete entry at that endpoint.
| Method | Result |
|---|---|
firstKey() | Smallest key |
lastKey() | Largest key |
firstEntry() | Entry with the smallest key |
lastEntry() | Entry with the largest key |
For example, if a leaderboard map uses scores as keys and player names as values, firstKey() returns the lowest score and lastKey() returns the highest score. The entry variants return the associated key and value together.
These operations can find an endpoint by following the leftmost or rightmost path through the tree rather than scanning every mapping. The time complexity is O(log n). If the map is empty, firstKey() and lastKey() throw NoSuchElementException; the entry-returning methods return null when no entry exists.
7. Neighbor Lookups: floorKey(), ceilingKey(), lowerKey(), and higherKey()
Navigation methods find the closest key relative to a supplied key. They are one of the main reasons to choose TreeMap.
| Method | Meaning |
|---|---|
floorKey(k) | Greatest key less than or equal to k |
ceilingKey(k) | Smallest key greater than or equal to k |
lowerKey(k) | Greatest key strictly less than k |
higherKey(k) | Smallest key strictly greater than k |
The distinction is whether an exact match is allowed. floorKey(50) can return 50 if it exists; lowerKey(50) skips it and returns the next smaller key. Similarly, ceilingKey(50) can return 50, while higherKey(50) requires a strictly greater key.
Each method has an entry-returning counterpart such as floorEntry(), ceilingEntry(), lowerEntry(), and higherEntry(). These return a Map.Entry rather than only the key.
If no key satisfies the requested relationship, the navigation method returns null. Callers must handle this possibility, especially when the supplied key is below the minimum or above the maximum.
8. Practical Example: Price Tiers
Imagine a storefront where shipping fees are associated with lower bounds of price tiers. A TreeMap can store each tier boundary as a key and its shipping fee as the value.
When a cart total arrives, floorKey(total) finds the greatest tier boundary that does not exceed the total. The corresponding value gives the applicable fee, assuming the map contains a suitable starting boundary for all supported totals.
This is more direct than scanning every tier and manually tracking the largest boundary below the cart total. The map contains only the tier boundaries, so lookup cost depends on the number of boundaries, not on the number of possible cart totals.
Always consider the no-match case. If the cart total is smaller than the smallest configured tier boundary, floorKey() returns null, and the application needs an explicit fallback policy.
9. Range Views: headMap(), tailMap(), and subMap()
TreeMap can expose a range of entries through view methods. These methods are especially useful when data is keyed by dates, scores, prices, or other ordered values.
| Method | Range in the common form |
|---|---|
headMap(k) | Keys strictly less than k |
tailMap(k) | Keys greater than or equal to k |
subMap(low, high) | Keys in the half-open range [low, high) |
The common two-argument subMap(low, high) includes the lower bound and excludes the upper bound. For example, a date range from May 10 up to May 13 includes May 10, May 11, and May 12, but not May 13.
The NavigableMap API also offers overloads that allow each endpoint to be configured as inclusive or exclusive. These are useful when the range definition requires different boundary behavior.
10. Range Views Are Backed by the Original Map
The objects returned by headMap(), tailMap(), and subMap() are views, not independent copies. They reflect the corresponding portion of the original map.
This has two important consequences:
- Changes made through the range view are reflected in the original map.
- Changes to the original map within the range are reflected in the view.
For example, calling put() on a subMap is allowed when the new key is within that view's bounds. Trying to insert a key outside the permitted range throws IllegalArgumentException.
Range views are efficient to create because they record the bounds rather than copying every matching entry. Iterating over the view takes time proportional to the number of entries visited, plus the cost of finding the starting point.
11. Using TreeMap for Date and Time Ranges
A map keyed by comparable date or time values can be a natural fit for range queries. For example, an order history can be keyed by LocalDate or a timestamp. A range view can then expose orders within a chosen window in sorted order.
If keys are date strings, use a consistent sortable representation such as ISO format. If date objects are available, a date type such as LocalDate can express the ordering more clearly and avoid string-format mistakes.
Remember that the upper endpoint of the basic subMap(low, high) form is exclusive. Choose the appropriate overload when both endpoints must be included, and check the application’s intended boundary rules before processing the returned view.
12. Null Keys and Null Values
With natural ordering, TreeMap does not permit null keys. Inserting a key requires comparison with other keys, and natural ordering cannot compare null with ordinary key values. This generally results in a NullPointerException during insertion or another operation that requires the comparison.
A custom comparator could define a special position for null, but that behavior depends on the comparator. For ordinary natural ordering, treat null keys as unsupported.
Null values, however, are allowed. This creates the same ambiguity found in other maps that permit null values: get(key) returning null may mean the key is absent or that the key exists with a null value. Use containsKey(key) when the difference matters.
13. Thread Safety
TreeMap is not synchronized by default. If multiple threads access a shared instance and one or more threads modify it, the application must provide appropriate coordination.
For applications that need a sorted map with concurrent access, ConcurrentSkipListMap is a common alternative to consider. It provides concurrent sorted-map behavior, although its guarantees and performance characteristics differ from those of TreeMap.
Do not confuse sorted ordering with thread safety: keeping keys in order does not make simultaneous writes safe.
14. Performance and Complexity
| Operation | Typical complexity | Explanation |
|---|---|---|
get() | O(log n) | Follows a path through the balanced tree |
put() | O(log n) | Searches for the insertion position and maintains balance |
remove() | O(log n) | Locates and removes a node while maintaining balance |
containsKey() | O(log n) | Uses key comparisons to search the tree |
| Navigation lookup | O(log n) | Finds a neighboring key through the tree |
| Create a range view | O(1) view setup | Records range bounds rather than copying all entries |
Iterate over k entries in a view | O(log n + k), typically | Locate the range start, then visit matching entries |
HashMap usually has expected O(1) lookup for well-distributed keys, so it is often faster when only direct lookup is needed. TreeMap pays logarithmic operation costs to maintain sorted keys and provide navigation and range capabilities.
If data is loaded once and then read many times, it can sometimes be efficient to load it into a HashMap and sort a key list for a particular report. If data changes frequently and ordered operations are common, repeatedly sorting after each change can become more expensive than maintaining a TreeMap.
15. TreeMap vs HashMap vs LinkedHashMap vs Hashtable
| Implementation | Iteration order | Typical lookup | Null key | Main reason to choose it |
|---|---|---|---|---|
HashMap | No guaranteed order | Expected O(1) | One null key allowed | General-purpose lookup |
LinkedHashMap | Insertion or access order | Expected O(1) | One null key allowed | Predictable encounter order |
TreeMap | Sorted by key | O(log n) | Not with natural ordering | Navigation and range queries |
Hashtable | No guaranteed order | Typically O(1) average | Not allowed | Legacy synchronized API |
TreeMap is the choice in this group when key order itself matters. LinkedHashMap remembers insertion or access order, which is different from sorting by key. HashMap remains the usual choice when ordering and navigation are unnecessary. Hashtable is a legacy class and is generally not the first choice for new code.
16. Common Mistakes
Mistake 1: Expecting constant-time lookup
TreeMap lookup is O(log n), not expected O(1). Its advantage is sorted order and navigation methods.
Mistake 2: Confusing insertion order with sorted order
LinkedHashMap follows insertion or access order. TreeMap follows key order.
Mistake 3: Forgetting the strict versus inclusive boundary
floorKey() and ceilingKey() allow equality; lowerKey() and higherKey() require a strict comparison.
Mistake 4: Forgetting that navigation can return null
If no key satisfies the requested relationship, the navigation method returns null. Always handle boundary cases.
Mistake 5: Treating a range view as a copy
Range views are backed by the original map. Updates can be visible from both directions, and inserting outside the view's bounds is not allowed.
Mistake 6: Assuming subMap(low, high) includes both endpoints
The basic two-key form includes the lower endpoint and excludes the upper endpoint. Use the inclusive/exclusive overload when needed.
Mistake 7: Using null keys with natural ordering
Natural ordering cannot compare null with ordinary keys. Avoid null keys unless a comparator explicitly defines and supports their ordering.
Mistake 8: Assuming TreeMap is thread-safe
Sorted order does not provide synchronization. Use appropriate coordination or consider a concurrent sorted map.
17. Best Practices
- Use
TreeMapwhen sorted key iteration or range queries are genuine requirements. - Prefer natural ordering for standard comparable types when it matches the required order.
- Supply a comparator when the required order differs from natural ordering.
- Use
floorKey()for “at or below” lookups andceilingKey()for “at or above” lookups. - Use
lowerKey()andhigherKey()when exact matches must be excluded. - Handle null returns from navigation methods.
- Understand whether range endpoints are inclusive or exclusive.
- Treat range views as live views rather than snapshots.
- Avoid null keys with natural ordering.
- Do not assume thread safety; coordinate shared access or choose a concurrent sorted-map implementation.
- Use
HashMapinstead when only direct lookup is required and ordering adds no value. - Consider whether sorting a snapshot is more suitable for a one-off report than maintaining sorted order on every update.
18. Quick Revision
TreeMapstores key-value pairs in sorted key order.- It uses a self-balancing red-black tree.
- Basic lookup, insertion, and removal are O(log n).
- Natural ordering is used by default; a
Comparatorcan customize it. firstKey()andlastKey()return the minimum and maximum keys.floorKey()returns the greatest key less than or equal to the input.ceilingKey()returns the smallest key greater than or equal to the input.lowerKey()andhigherKey()require strict inequality.headMap(),tailMap(), andsubMap()expose range views.- Range views are backed by the original map.
- The basic
subMap(low, high)includes the low endpoint and excludes the high endpoint. - Natural ordering does not support null keys; null values are allowed.
TreeMapis not synchronized by default.- Choose
TreeMapfor key order and range operations,LinkedHashMapfor encounter order, andHashMapfor general unordered lookup.
19. Conclusion
TreeMap is especially valuable when keys are not merely identifiers but also define an order that the application needs to use. Its balanced tree keeps entries sorted and enables endpoint lookups, neighbor searches, and efficient range views.
Its logarithmic performance is the trade-off for maintaining order. For applications that need sorted dates, price tiers, leaderboards, or key-range processing, that trade is often worthwhile. For plain lookup without ordering needs, HashMap is generally the simpler default; for insertion order, LinkedHashMap is the better fit.