NavigableMap and NavigableSet in Java – HeadMap, TailMap, and More Explained

Illustration for NavigableMap and NavigableSet in Java – HeadMap, TailMap, and More Explained
By Last updated:

In Java, ordered collections that allow navigation and range views are powered by two powerful interfaces: NavigableMap and NavigableSet. These are advanced extensions of SortedMap and SortedSet that provide fine-grained control over ordering, range queries, and element access.

If you've ever needed to find the closest value lower or higher than a key, or fetch a subset of elements within a range, then NavigableMap and NavigableSet are for you. Understanding these APIs can greatly enhance your ability to build efficient and readable logic for real-world systems like search engines, cache indexes, scheduling, or stock price analysis.


What Is It?

NavigableMap<K, V> extends SortedMap<K, V> and provides navigation methods such as lowerEntry(), floorEntry(), ceilingEntry(), higherEntry(), and view-returning methods like headMap() and tailMap().

Common Implementation

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

Key Methods

Method Description
lowerEntry(key) Entry less than the given key
floorEntry(key) Entry ≤ given key
ceilingEntry(key) Entry ≥ given key
higherEntry(key) Entry greater than key
headMap(key, inclusive) Submap strictly less or up to key
tailMap(key, inclusive) Submap from key onwards
descendingMap() Reversed view of the map

Example

TreeMap<Integer, String> scores = new TreeMap<>();
scores.put(90, "A");
scores.put(80, "B");
scores.put(70, "C");

System.out.println(scores.floorEntry(85)); // 80=B
System.out.println(scores.ceilingEntry(85)); // 90=A

What Is It?

NavigableSet<E> extends SortedSet<E> and supports navigation-based methods like lower(), floor(), ceiling(), higher().

Common Implementation

NavigableSet<Integer> set = new TreeSet<>();

Key Methods

Method Description
lower(e) Greatest element < e
floor(e) Greatest element ≤ e
ceiling(e) Least element ≥ e
higher(e) Least element > e
headSet(e, inclusive) Elements < or ≤ e
tailSet(e, inclusive) Elements ≥ or > e
descendingSet() Reversed view of the set

Example

TreeSet<Integer> numbers = new TreeSet<>();
numbers.addAll(List.of(10, 20, 30, 40));

System.out.println(numbers.lower(25));  // 20
System.out.println(numbers.higher(25)); // 30

Internal Working and Performance

Both interfaces are backed by balanced binary search trees (usually Red-Black Tree via TreeMap and TreeSet), ensuring O(log n) time complexity for most operations.

Operation Time Complexity
insert/delete O(log n)
floor/ceiling O(log n)
iteration O(n)

Real-World Use Cases

  • Leaderboard systems – Retrieve top N scores (descendingMap().entrySet())
  • Event scheduling – Use ceilingEntry() to find the next event
  • Range queries – Analyze stock prices in a given time window
  • Autocomplete systems – Use tailSet(prefix) to get suggestions

Java Syntax and Functional Examples

NavigableMap<Integer, String> map = new TreeMap<>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");

map.subMap(1, true, 2, true) // includes 1 and 2
    .forEach((k, v) -> System.out.println(k + " => " + v));
NavigableSet<String> set = new TreeSet<>();
set.addAll(List.of("apple", "banana", "mango"));

set.tailSet("banana", true)
   .forEach(System.out::println); // banana, mango

Java Version Tracker

📌 What's New in Java?

  • Java 8
    • Introduced functional support (forEach, Streams)
    • Seamless integration with entrySet().stream() and filter()
  • Java 9
    • Map.of() and immutable collections (though not Navigable)
  • Java 10+
    • Type inference (var) makes syntax cleaner
  • Java 21
    • No direct enhancements to NavigableMap/Set, but TreeMap performance improves indirectly via GC enhancements and CPU optimizations

Comparisons

Feature NavigableMap NavigableSet
Backed by TreeMap TreeSet
Type Map<K, V> Set
Range views headMap, tailMap headSet, tailSet
Floor/Ceiling methods Yes Yes
Key-based lookup Yes (K → V) No (value-only)
Reverse order view descendingMap() descendingSet()

Best Practices

  • Use TreeMap/TreeSet only when ordering matters; else prefer HashMap/HashSet
  • Leverage subMap() and headMap() for range queries instead of manual iteration
  • Be cautious when modifying maps/sets during iteration
  • Use immutability wrappers if sharing maps across threads (Collections.unmodifiableMap())

Anti-Patterns to Avoid

  • Using NavigableMap for key types without natural ordering or a custom Comparator
  • Forgetting that subMap() views are backed — changes affect the original map
  • Using floor()/ceiling() on empty collections — may return null or throw

Refactoring Legacy Code

  • Replace manual sorted array or list search logic with NavigableMap methods (ceilingEntry, floorEntry)
  • Convert nested if conditions checking range into subMap() or tailMap()

Functional Programming with Streams

TreeMap<Integer, String> map = new TreeMap<>();
map.put(10, "ten");
map.put(20, "twenty");

map.entrySet()
   .stream()
   .filter(e -> e.getKey() > 15)
   .forEach(System.out::println);
TreeSet<String> set = new TreeSet<>(List.of("A", "B", "C"));
set.stream()
   .map(String::toLowerCase)
   .forEach(System.out::println);

Conclusion and Key Takeaways

  • NavigableMap and NavigableSet offer powerful tools for sorted, navigable collections.
  • Use them when ordering and range queries are key to your logic.
  • Understand that these interfaces are not replacements for HashMap/HashSet, but specialized alternatives.
  • Leverage methods like floor(), ceiling(), headMap() for precise control over data access.

FAQ – NavigableMap and NavigableSet

  1. Can I use null keys in NavigableMap?
    No. TreeMap (most common impl.) does not allow null keys.

  2. What’s the difference between headMap and tailMap?
    headMap() gives entries less than a key; tailMap() gives entries from the key onward.

  3. Do these interfaces maintain insertion order?
    No. They maintain natural or comparator-based sorted order.

  4. Are the views like subMap() backed or copied?
    They’re backed views — changes reflect in the original structure.

  5. Can I use these in concurrent environments?
    Not directly. Use ConcurrentSkipListMap or sync wrappers.

  6. Are floorEntry() and floor() the same?
    Yes, but floorEntry() returns a map entry; floor() returns just the value (or key in sets).

  7. How do I reverse the order?
    Use descendingMap() or descendingSet().

  8. What’s the performance difference with HashMap?
    HashMap is faster for key-value access; TreeMap offers ordered navigation at a small cost.

  9. Can I serialize TreeMap/TreeSet?
    Yes, both implement Serializable.

  10. What happens on duplicate keys in NavigableMap?
    New values replace old ones; keys remain unique.