00:00

NavigableSet Interface in Java – Guide with Examples

NavigableSet is a SortedSet with extra methods for moving around the sorted elements: finding the closest element below or above a value, taking ranges with the bounds you choose, walking backwards, and removing the smallest or largest element.

It was added to java.util in Java 6 and is implemented by TreeSet and ConcurrentSkipListSet. This tutorial covers every NavigableSet method with short, practical examples.

1. What Is a NavigableSet?

Imagine buying shoes. If size 8 is out of stock, the shop assistant checks the nearest sizes: "the biggest size below 8" or "the smallest size above 8". A NavigableSet answers exactly these questions in O(log n) time.

  • All features of SortedSet: sorted unique elements, first(), last() and range views.
  • Nearest-element lookups: lower(), floor(), ceiling(), higher().
  • Flexible ranges: choose whether each bound is inclusive.
  • Reverse order: descendingSet() and descendingIterator().
  • Retrieve and remove: pollFirst() and pollLast().

2. Declaration and Hierarchy

java.util.NavigableSet
public interface NavigableSet<E> extends SortedSet<E>
Hierarchy
Set
 └─ SortedSet
     └─ NavigableSet
         ├─ TreeSet                (not thread-safe)
         └─ ConcurrentSkipListSet  (thread-safe)

3. Finding the Nearest Element

The four lookup methods differ only in whether they include the value itself and in which direction they look. Each returns null when no element qualifies.

NavigableLookups.java
import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;

public class NavigableLookups {
    public static void main(String[] args) {
        NavigableSet<Integer> sizes = new TreeSet<>(List.of(6, 7, 8, 9, 10, 11));

        for (int wanted : new int[] {8, 12, 5}) {
            System.out.printf("wanted=%-2d lower=%-4s floor=%-4s ceiling=%-4s higher=%s%n",
                    wanted, sizes.lower(wanted), sizes.floor(wanted),
                    sizes.ceiling(wanted), sizes.higher(wanted));
        }
    }
}
Output
wanted=8  lower=7    floor=8    ceiling=8    higher=9
wanted=12 lower=11   floor=11   ceiling=null higher=null
wanted=5  lower=null floor=null ceiling=6    higher=6
MethodReturns the…Example with {6…11}, value 8
lower(e)greatest element < e7
floor(e)greatest element ≤ e8
ceiling(e)smallest element ≥ e8
higher(e)smallest element > e9
Watch out: these methods return null when nothing matches. Assigning the result to an int variable unboxes it and throws NullPointerException; keep it as Integer and check for null.

4. Range Views with Inclusive Bounds

SortedSet range views always include the lower bound and exclude the upper bound. NavigableSet adds overloads where you decide for each bound:

NavigableRanges.java
import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;

public class NavigableRanges {
    public static void main(String[] args) {
        NavigableSet<Integer> ages = new TreeSet<>(List.of(12, 17, 18, 25, 40, 59, 60, 72));

        System.out.println("Under 18:          " + ages.headSet(18, false));
        System.out.println("18 and under:      " + ages.headSet(18, true));
        System.out.println("Seniors (60+):     " + ages.tailSet(60, true));
        System.out.println("Adults 18 to 59:   " + ages.subSet(18, true, 59, true));
        System.out.println("Between 18 and 60: " + ages.subSet(18, false, 60, false));
    }
}
Output
Under 18:          [12, 17]
18 and under:      [12, 17, 18]
Seniors (60+):     [60, 72]
Adults 18 to 59:   [18, 25, 40, 59]
Between 18 and 60: [25, 40, 59]

Like all range views, these are backed by the original set: changes in one appear in the other.

5. Descending Order and Polling

descendingSet() returns a reverse-ordered view, and descendingIterator() walks from the largest element to the smallest. pollFirst() and pollLast() remove and return the ends, returning null on an empty set instead of throwing.

DescendingAndPoll.java
import java.util.Iterator;
import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;

public class DescendingAndPoll {
    public static void main(String[] args) {
        NavigableSet<String> versions = new TreeSet<>(List.of("1.0", "1.2", "2.0", "2.1", "3.0"));

        System.out.println("descendingSet(): " + versions.descendingSet());

        System.out.print("descendingIterator(): ");
        Iterator<String> it = versions.descendingIterator();
        while (it.hasNext()) System.out.print(it.next() + " ");
        System.out.println();

        // pollFirst()/pollLast() remove as they return
        System.out.println("pollLast():  " + versions.pollLast());
        System.out.println("pollFirst(): " + versions.pollFirst());
        System.out.println("Remaining:   " + versions);

        NavigableSet<String> empty = new TreeSet<>();
        System.out.println("pollFirst() on empty set: " + empty.pollFirst());
    }
}
Output
descendingSet(): [3.0, 2.1, 2.0, 1.2, 1.0]
descendingIterator(): 3.0 2.1 2.0 1.2 1.0 
pollLast():  3.0
pollFirst(): 1.0
Remaining:   [1.2, 2.0, 2.1]
pollFirst() on empty set: null

Note that strings are compared character by character, so "10.0" would sort before "2.0". For real version numbers, use a comparator that compares the numeric parts.

6. Practical Example: Closest Value

Combining floor() and ceiling() finds the element nearest to any target, for example the nearest bus stop:

ClosestValue.java
import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;

public class ClosestValue {
    // Returns the element closest to target (the smaller one on a tie)
    static Integer closest(NavigableSet<Integer> set, int target) {
        Integer below = set.floor(target);
        Integer above = set.ceiling(target);
        if (below == null) return above;
        if (above == null) return below;
        return (target - below <= above - target) ? below : above;
    }

    public static void main(String[] args) {
        NavigableSet<Integer> busStops = new TreeSet<>(List.of(0, 450, 1200, 2000, 3100));
        for (int position : new int[] {500, 1700, 2600, 5000}) {
            System.out.println("At " + position + " m, nearest stop: " + closest(busStops, position) + " m");
        }
    }
}
Output
At 500 m, nearest stop: 450 m
At 1700 m, nearest stop: 2000 m
At 2600 m, nearest stop: 3100 m
At 5000 m, nearest stop: 3100 m

7. All NavigableSet Methods

MethodDescription
lower(e), floor(e), ceiling(e), higher(e)Nearest element below or above e, or null.
pollFirst(), pollLast()Remove and return the lowest or highest element, or null if empty.
descendingSet()Reverse-order view of the set.
descendingIterator()Iterator from highest to lowest.
headSet(to, inclusive)View of elements below to (optionally including it).
tailSet(from, inclusive)View of elements above from (optionally including it).
subSet(from, fromInc, to, toInc)View of elements between two bounds.
Inherited from SortedSetfirst(), last(), headSet(to), tailSet(from), subSet(from, to), comparator().
Java 21 (sequenced)getFirst(), getLast(), removeFirst(), removeLast(), reversed().

8. ConcurrentSkipListSet: A Thread-Safe NavigableSet

ConcurrentSkipListSet implements NavigableSet using a skip list, a layered linked structure that gives O(log n) operations and allows many threads to read and write at the same time without one big lock.

ConcurrentSortedSet.java
import java.util.NavigableSet;
import java.util.concurrent.ConcurrentSkipListSet;

public class ConcurrentSortedSet {
    public static void main(String[] args) throws InterruptedException {
        NavigableSet<Integer> ids = new ConcurrentSkipListSet<>();

        Thread evens = new Thread(() -> { for (int i = 0; i < 1000; i += 2) ids.add(i); });
        Thread odds  = new Thread(() -> { for (int i = 1; i < 1000; i += 2) ids.add(i); });
        evens.start(); odds.start();
        evens.join();  odds.join();

        System.out.println("size=" + ids.size() + ", first=" + ids.first() + ", last=" + ids.last());
        System.out.println("ceiling(500)=" + ids.ceiling(500) + ", headSet(5)=" + ids.headSet(5));
    }
}
Output
size=1000, first=0, last=999
ceiling(500)=500, headSet(5)=[0, 1, 2, 3, 4]

Its iterators are weakly consistent: they never throw ConcurrentModificationException. One caveat: size() walks the whole set, so it is O(n), not O(1).

9. NavigableSet vs SortedSet

FeatureSortedSetNavigableSet
Introduced inJDK 1.2Java 6
Nearest-element lookupsNoYes
Range boundsFixed: lower inclusive, upper exclusiveEach bound configurable
Reverse viewreversed() (Java 21)descendingSet()
Remove ends safelyNopollFirst(), pollLast()

10. Common Use Cases

  • Nearest match: closest store, size, price or time slot.
  • Ranges and bands: age groups, salary brackets, score grades.
  • Leaderboards: top or bottom entries with descendingSet().
  • Simple ordered work lists: take the smallest item with pollFirst() (a PriorityQueue allows duplicates, which a set does not).
  • Interval and calendar logic: find the event before or after a given time.

11. Summary

  • NavigableSet extends SortedSet with navigation methods.
  • lower/floor/ceiling/higher find the nearest element, returning null if none exists.
  • Range methods accept inclusive flags for each bound.
  • descendingSet() gives a reverse view; pollFirst() and pollLast() remove the ends safely.
  • Implementations: TreeSet (single thread) and ConcurrentSkipListSet (concurrent).

Related Tutorials

Reference: Java SE 21 API: java.util.NavigableSet