TreeSet & Sorted Sets
TreeSet & Sorted Sets
A HashSet gives you fast membership tests but makes no promises about order. Sometimes you need both uniqueness and a predictable sorted order — that is exactly what TreeSet provides. Understanding when and why to reach for TreeSet instead of HashSet is an important step in writing expressive, efficient Java code.
What TreeSet is Under the Hood
TreeSet<E> is a NavigableSet backed by a red-black tree. Every insertion and removal is O(log n) instead of the near-O(1) of a hash-based set. The trade-off is that iteration always visits elements in ascending sorted order, and a rich family of navigation methods becomes available.
TreeSet implements NavigableSet, which extends SortedSet, which extends Set. When you only need sorted iteration, type your variable as SortedSet. When you need navigation methods (floor, ceiling, etc.), use NavigableSet.
Natural Ordering with Comparable
When you construct a TreeSet without passing a comparator, it uses natural ordering — the ordering defined by the elements' own Comparable<T> implementation. All Java wrapper types (Integer, String, LocalDate, …) already implement Comparable, so this works out of the box:
For your own classes you implement Comparable<T> by overriding compareTo. The contract: return a negative number when this is less-than the argument, zero when equal, and a positive number when greater.
equals() to each other — otherwise a TreeSet will treat them as duplicates while a HashSet would not, leading to confusing bugs when you switch implementations.
NavigableSet Operations
TreeSet exposes powerful navigation methods that have no equivalent on HashSet:
first()/last()— smallest / largest element.floor(e)— greatest element ≤ e, ornull.ceiling(e)— smallest element ≥ e, ornull.lower(e)— greatest element strictly less than e.higher(e)— smallest element strictly greater than e.headSet(to)/tailSet(from)— views of elements before / from a value.subSet(from, to)— a live view of the range [from, to).pollFirst()/pollLast()— remove and return the smallest / largest.
headSet, tailSet, and subSet return backed views. Mutations through the view propagate to the original set and vice versa — useful for range deletions or range iteration without copying.
Descending Order
Need to iterate from highest to lowest without writing a custom comparator? Call descendingSet():
When to Choose TreeSet over HashSet
- You need to iterate elements in sorted order.
- You need range queries (all elements between X and Y).
- You need the minimum or maximum element quickly.
- Your elements implement
Comparable(or you supply aComparator).
If none of these apply, stick with HashSet — its O(1) operations beat TreeSet's O(log n) for pure membership tests at large scale.
Summary
TreeSet stores unique elements in sorted order using a red-black tree. Natural ordering relies on Comparable; supply a Comparator when you need a different order or when the element type is not Comparable. The NavigableSet API — floor, ceiling, headSet, subSet, and friends — makes range-based lookups expressive and efficient. Next we will look at the map equivalent of TreeSet: TreeMap and LinkedHashMap.