The TreeSet Class
Objective
Understand TreeSet, the NavigableSet implementation backed by a tree structure: elements are kept in ascending order automatically, by natural ordering or a supplied Comparator, in exchange for logarithmic rather than constant-time operations.
Use Cases
- Needing elements to always be in sorted order without a separate sort step after every insert.
- Retrieving the minimum/maximum element, or a whole sorted range, directly rather than scanning.
- Closest-match lookups (smallest ≥ x, largest ≤ x) via the
NavigableSetmethods — see the Set interface concept for the fullceiling/floor/higher/lowerbreakdown. - Producing a deduplicated and sorted view of arbitrary input in one structure.
Deep Dive
TreeSet extends AbstractSet, implements NavigableSet
javaclass TreeSet<E>Four constructors:
javaTreeSet<String> a = new TreeSet<>(); // natural ordering
TreeSet<String> b = new TreeSet<>(List.of("C", "A", "B")); // from a collection, natural ordering
TreeSet<String> c = new TreeSet<>(Comparator.reverseOrder()); // custom ordering
TreeSet<String> d = new TreeSet<>((SortedSet<String>) someSortedSet);Ascending order is automatic
javaTreeSet<String> ts = new TreeSet<>();
ts.add("C"); ts.add("A"); ts.add("B"); ts.add("E"); ts.add("F"); ts.add("D");
System.out.println(ts); // [A, B, C, D, E, F] — sorted, regardless of insertion orderWatch it happen: add() landing in sorted position
Same six elements, same arrival order as above — each add() lands directly at its final ascending-order slot, not at the end like a plain ArrayList would:
Slot 0 is the smallest element seen across the whole set, not the first one added — that's the difference between NavigableSet's ordering guarantee and a LinkedHashSet's insertion order.
Range queries via NavigableSet
javats.subSet("C", "F"); // [C, D, E] — >= C, < FsubSet/headSet/tailSet return a live NavigableSet view backed by ts, not a copy — see the Set interface concept for the inclusive-bound overloads and the closest-match methods (ceiling, floor, higher, lower).
Trade-offs
O(log n) operations, not O(1) —
add/remove/containswalk the tree to maintain sorted order, so aTreeSetis consistently slower than aHashSetfor plain membership testing; pay that cost only when the ordering is actually used.Uniqueness is decided by
compareTo()(or the suppliedComparator), not byequals()/hashCode()— two elements the ordering considers equal (compareTo() == 0) are treated as duplicates even ifequals()would say otherwise:javarecord Item(String name, int rank) {} Comparator<Item> byRank = Comparator.comparingInt(Item::rank); TreeSet<Item> ts = new TreeSet<>(byRank); ts.add(new Item("a", 1)); ts.add(new Item("b", 1)); // rejected — compareTo() says rank 1 == rank 1, even though not equals() System.out.println(ts.size()); // 1Elements must be mutually comparable, and nothing enforces that at compile time when no
Comparatoris supplied — inserting an object that can't actually be compared to the others compiles fine and fails at the point a comparison is forced, as aClassCastExceptionthrown fromcompareTo(), not fromTreeSetitself.