NavigableMap Interface in Java – Guide with Examples
NavigableMap is a SortedMap with methods for finding the nearest key to any value, taking key ranges with the bounds you choose, walking the map backwards, and removing the first or last entry.
It was added to java.util in Java 6 and is implemented by TreeMap and ConcurrentSkipListMap. These methods turn many "look up by range" problems, such as rates by date, floors by number or jobs by time, into one line of code.
1. What Is a NavigableMap?
A NavigableMap answers questions a plain map cannot: "What was the interest rate on 10 March?" (the latest change on or before that date), "Which floor is just below floor 6?" or "Give me every entry from 2 to 8, including both ends".
- Everything from SortedMap: sorted keys,
firstKey(),lastKey(), range views. - Nearest-key lookups in two flavours: returning the key (
floorKey()) or the whole entry (floorEntry()). - Ranges with inclusive or exclusive bounds.
- Reverse views:
descendingMap()anddescendingKeySet(). - Polling:
pollFirstEntry()andpollLastEntry()remove and return the ends.
2. Declaration and Hierarchy
public interface NavigableMap<K, V> extends SortedMap<K, V>
Map
└─ SortedMap
└─ NavigableMap
├─ TreeMap
└─ ConcurrentNavigableMap
└─ ConcurrentSkipListMap
3. Lookup by Date: floorEntry()
Store each change date as a key. floorEntry(date) then returns the change that was in effect on that date:
import java.time.LocalDate;
import java.util.NavigableMap;
import java.util.TreeMap;
public class RateOnDate {
public static void main(String[] args) {
// Date a new interest rate starts -> rate in %
NavigableMap<LocalDate, Double> rates = new TreeMap<>();
rates.put(LocalDate.of(2024, 1, 1), 6.5);
rates.put(LocalDate.of(2024, 7, 1), 6.25);
rates.put(LocalDate.of(2025, 2, 15), 6.0);
for (LocalDate d : new LocalDate[] {
LocalDate.of(2024, 3, 10), LocalDate.of(2024, 7, 1), LocalDate.of(2025, 6, 30), LocalDate.of(2023, 12, 31)}) {
var entry = rates.floorEntry(d); // the latest change on or before d
System.out.println(d + " -> " + (entry == null ? "no rate yet" : entry.getValue() + "% (since " + entry.getKey() + ")"));
}
}
}
2024-03-10 -> 6.5% (since 2024-01-01) 2024-07-01 -> 6.25% (since 2024-07-01) 2025-06-30 -> 6.0% (since 2025-02-15) 2023-12-31 -> no rate yet
The last date is before the first rate, so floorEntry() returned null. Always handle that case.
4. All Navigation Methods in Action
import java.util.NavigableMap;
import java.util.TreeMap;
public class NavigableMapMethods {
public static void main(String[] args) {
NavigableMap<Integer, String> floors = new TreeMap<>();
floors.put(0, "Lobby");
floors.put(2, "Cafe");
floors.put(5, "Offices");
floors.put(8, "Gym");
floors.put(10, "Roof");
System.out.println("lowerKey(5): " + floors.lowerKey(5));
System.out.println("floorEntry(6): " + floors.floorEntry(6));
System.out.println("ceilingKey(6): " + floors.ceilingKey(6));
System.out.println("higherEntry(8): " + floors.higherEntry(8));
System.out.println("headMap(5, true): " + floors.headMap(5, true));
System.out.println("tailMap(5, false): " + floors.tailMap(5, false));
System.out.println("subMap(2, true, 8, true): " + floors.subMap(2, true, 8, true));
System.out.println("descendingMap(): " + floors.descendingMap());
System.out.println("descendingKeySet(): " + floors.descendingKeySet());
System.out.println("navigableKeySet().floor(7): " + floors.navigableKeySet().floor(7));
}
}
lowerKey(5): 2
floorEntry(6): 5=Offices
ceilingKey(6): 8
higherEntry(8): 10=Roof
headMap(5, true): {0=Lobby, 2=Cafe, 5=Offices}
tailMap(5, false): {8=Gym, 10=Roof}
subMap(2, true, 8, true): {2=Cafe, 5=Offices, 8=Gym}
descendingMap(): {10=Roof, 8=Gym, 5=Offices, 2=Cafe, 0=Lobby}
descendingKeySet(): [10, 8, 5, 2, 0]
navigableKeySet().floor(7): 5
5. NavigableMap Methods
| Method | Returns |
|---|---|
lowerKey(k), lowerEntry(k) | Greatest key strictly less than k. |
floorKey(k), floorEntry(k) | Greatest key less than or equal to k. |
ceilingKey(k), ceilingEntry(k) | Smallest key greater than or equal to k. |
higherKey(k), higherEntry(k) | Smallest key strictly greater than k. |
firstEntry(), lastEntry() | Smallest and largest entries, or null if empty. |
pollFirstEntry(), pollLastEntry() | Remove and return the smallest or largest entry. |
headMap(to, inclusive) | View of keys below to. |
tailMap(from, inclusive) | View of keys above from. |
subMap(from, fromInc, to, toInc) | View of keys between two bounds. |
descendingMap() | Reverse-order view of the map. |
navigableKeySet(), descendingKeySet() | Keys as a NavigableSet, ascending or descending. |
The …Entry methods return immutable snapshot entries: calling setValue() on them throws UnsupportedOperationException. Use put() to change a value.
6. Processing Entries in Order with pollFirstEntry()
pollFirstEntry() removes the smallest key and returns its entry, which makes a sorted map work like a simple time-ordered queue:
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
public class ScheduledJobs {
public static void main(String[] args) {
NavigableMap<Integer, String> jobs = new TreeMap<>(); // run time (minute of day) -> job
jobs.put(540, "send reports");
jobs.put(60, "backup database");
jobs.put(720, "clear cache");
jobs.put(300, "rotate logs");
int now = 600;
System.out.println("Jobs due by minute " + now + ":");
Map.Entry<Integer, String> next;
while ((next = jobs.firstEntry()) != null && next.getKey() <= now) {
jobs.pollFirstEntry(); // remove it as we run it
System.out.println(" running " + next.getValue() + " (scheduled at " + next.getKey() + ")");
}
System.out.println("Still waiting: " + jobs);
}
}
Jobs due by minute 600:
running backup database (scheduled at 60)
running rotate logs (scheduled at 300)
running send reports (scheduled at 540)
Still waiting: {720=clear cache}
Keys are unique, so two jobs at the same minute would overwrite each other. Use a NavigableMap<Integer, List<String>> or a PriorityQueue when duplicates are possible.
7. ConcurrentSkipListMap
For sorted data shared between threads, ConcurrentSkipListMap implements ConcurrentNavigableMap. It allows concurrent reads and writes without one global lock and offers all navigation methods.
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
public class ConcurrentSortedMap {
public static void main(String[] args) throws InterruptedException {
ConcurrentNavigableMap<Integer, String> orders = new ConcurrentSkipListMap<>();
Thread a = new Thread(() -> { for (int i = 1; i <= 500; i += 2) orders.put(i, "A"); });
Thread b = new Thread(() -> { for (int i = 2; i <= 500; i += 2) orders.put(i, "B"); });
a.start(); b.start();
a.join(); b.join();
System.out.println("size=" + orders.size() + ", firstKey=" + orders.firstKey() + ", lastKey=" + orders.lastKey());
System.out.println("headMap(4): " + orders.headMap(4));
System.out.println("ceilingKey(250): " + orders.ceilingKey(250));
}
}
size=500, firstKey=1, lastKey=500
headMap(4): {1=A, 2=B, 3=A}
ceilingKey(250): 250
Its iterators are weakly consistent (no ConcurrentModificationException), it does not allow null keys or values, and its size() is O(n).
8. NavigableMap vs SortedMap
| Feature | SortedMap | NavigableMap |
|---|---|---|
| Introduced | JDK 1.2 | Java 6 |
| Nearest-key lookups | No | Yes, keys and entries |
| Range bounds | Lower inclusive, upper exclusive | Each bound configurable |
| Reverse view | reversed() (Java 21) | descendingMap() |
| Remove smallest/largest | pollFirstEntry() (Java 21) | pollFirstEntry(), pollLastEntry() |
9. Common Use Cases
- Values that change over time: prices, rates or configuration in effect on a date (
floorEntry). - Bands and tiers: tax slabs, grades, shipping costs.
- Nearest neighbour: closest available seat, slot or server.
- Time-ordered processing: scheduled jobs, event timelines.
- Leaderboards: top or bottom scores via
descendingMap().
10. Summary
- NavigableMap extends SortedMap with navigation methods.
lower,floor,ceilingandhighercome in key and entry versions and returnnullwhen nothing matches.- Range views accept inclusive flags;
descendingMap()gives a reverse view. pollFirstEntry()andpollLastEntry()remove the ends.- TreeMap is the standard implementation; ConcurrentSkipListMap is the concurrent one.
Related Tutorials
Reference: Java SE 21 API: java.util.NavigableMap
Frequently Asked Questions (FAQ)
Q1. What is a NavigableMap in Java?
NavigableMap is an interface in java.util that extends SortedMap with methods to find the nearest keys, take ranges with chosen bounds, view the map in descending order, and remove the first or last entry.
Q2. Which classes implement NavigableMap?
TreeMap and ConcurrentSkipListMap. Views such as subMap() and descendingMap() of these maps are NavigableMaps too.
Q3. What is the difference between floorKey() and floorEntry()?
Both find the greatest key ≤ the argument. floorKey() returns just the key; floorEntry() returns the key and its value as a Map.Entry, saving a second lookup.
Q4. What is the difference between lowerKey() and floorKey()?
lowerKey(k) returns the greatest key strictly less than k. floorKey(k) returns k itself if it exists, otherwise the greatest key below it.
Q5. What is the difference between ceilingKey() and higherKey()?
ceilingKey(k) returns the smallest key ≥ k; higherKey(k) returns the smallest key strictly greater than k.
Q6. What do the navigation methods return when nothing matches?
null. Check the result before using it, especially before calling getValue() on an entry.
Q7. Can I call setValue() on the entry returned by floorEntry()?
No. Entries returned by floorEntry(), firstEntry(), pollFirstEntry() and similar methods are immutable snapshots; setValue() throws UnsupportedOperationException. Use map.put(entry.getKey(), newValue).
Q8. How do I get a range that includes both ends?
Use subMap(from, true, to, true). The flags control whether each bound is included.
Q9. Is descendingMap() a copy?
No, it is a live reverse-order view. Changes made to it or to the original map are visible in both.
Q10. What is the time complexity of NavigableMap methods in TreeMap?
All lookup and polling methods, as well as get, put and remove, are O(log n). Iterating a view costs time proportional to the number of entries it contains.
Q11. What is ConcurrentSkipListMap?
A thread-safe implementation of ConcurrentNavigableMap based on a skip list. It offers O(log n) operations, concurrent access without a global lock, and weakly consistent iterators. It does not allow null keys or values.
Q12. What is the difference between NavigableMap and SortedMap?
NavigableMap extends SortedMap. SortedMap has endpoints and fixed-bound ranges; NavigableMap adds nearest-key lookups, entry-returning methods, polling, descending views and ranges with configurable bounds.
Q13. How do I find the value in effect at a given time?
Key the map by the start time of each value and call floorEntry(time). The returned entry is the most recent change at or before that time, or null if none exists.
Q14. How do I iterate a NavigableMap from largest to smallest key?
for (Map.Entry<Integer, String> e : map.descendingMap().entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue());
}
Q15. What does navigableKeySet() return?
A NavigableSet view of the keys in ascending order. You can call set navigation methods such as floor() and headSet() on it, and removing a key from it removes the entry from the map.
Q16. Can a NavigableMap contain duplicate keys?
No. Like any map, each key appears once. Keys that compare as equal (comparison returns 0) are treated as the same key.
Q17. Is TreeMap the only single-threaded NavigableMap?
In the JDK, yes. Its sub-map and descending views are also NavigableMaps, but they are backed by the same TreeMap.
Q18. Does NavigableMap allow null keys?
TreeMap with natural ordering and ConcurrentSkipListMap do not. That lets the navigation methods use null safely to mean "no matching key".
Q19. Can I use pollFirstEntry() to build a queue?
Yes, for items with unique, comparable keys, such as timestamps. If keys can repeat, store a list per key or use a PriorityQueue, which allows duplicates.
Q20. When was NavigableMap introduced?
In Java 6 (JDK 1.6), together with NavigableSet, ConcurrentNavigableMap and ConcurrentSkipListMap.