The Set Interface
Objective
Understand Set and the two interfaces that refine it: Set is a Collection that forbids duplicate elements; SortedSet adds ascending order on top of that; NavigableSet adds closest-match lookups (ceiling, floor, higher, lower) and range views on top of SortedSet.
Use Cases
- Storing a group of values where membership is all that matters, and duplicates should be silently rejected rather than tracked.
- Building a small, fixed, unmodifiable set quickly with
Set.of(). - Keeping a collection in ascending order automatically, without a separate sort step (
SortedSet). - Getting the least or greatest element, or a whole range of elements, directly from the set instead of iterating (
SortedSet/NavigableSet). - Finding the closest match to a value that may not itself be in the set — the smallest element
>=it, or the largest<=it (NavigableSet). - Walking a set from greatest to least without maintaining a second, reverse-ordered structure (
NavigableSet.descendingSet()).
Deep Dive
Set extends Collection: no duplicates
javainterface Set<E>Set declares no methods of its own beyond what Collection already has — the contract is entirely behavioral. add() returns false, instead of throwing, when the element is already present:
javaSet<String> names = new HashSet<>();
names.add("Ann"); // true, added
names.add("Ann"); // false, already a member — not an errorUnmodifiable sets: Set.of()
Beginning with JDK 9, Set includes the of() factory method, with the same 12 overloads as List.of() and Collection.of()-style factories (zero through ten arguments, plus varargs):
javaSet<String> empty = Set.of();
Set<String> one = Set.of("Ann");
Set<String> many = Set.of("Ann", "Bob", "Cid");Every version returns an unmodifiable, value-based set; null elements are not allowed.
SortedSet: ascending order
javainterface SortedSet<E>A SortedSet keeps its elements sorted, either by their natural ordering or by a Comparator supplied when the set was created:
javaSortedSet<Integer> nums = new TreeSet<>(List.of(5, 1, 3));
nums.comparator(); // null here — natural ordering is in use
nums.first(); // 1
nums.last(); // 5SortedSet.copyOf(Collection<? extends E> from) returns an unmodifiable, value-based set with the same elements as from.
SortedSet range views: headSet, subSet, tailSet
javaSortedSet<Integer> nums = new TreeSet<>(List.of(1, 3, 5, 7, 9));
nums.headSet(5); // [1, 3] — elements < 5
nums.subSet(3, 7); // [3, 5] — elements >= 3 and < 7
nums.tailSet(5); // [5, 7, 9] — elements >= 5Each of these returns a SortedSet backed by the invoking set over that range, not a copy.
NavigableSet: closest-match lookups
javainterface NavigableSet<E>NavigableSet extends SortedSet and adds methods that search for the closest element to a given value, whether or not that exact value is present:
javaNavigableSet<Integer> nums = new TreeSet<>(List.of(1, 3, 5, 7, 9));
nums.ceiling(4); // 5 — smallest element >= 4
nums.floor(4); // 3 — largest element <= 4
nums.higher(5); // 7 — smallest element > 5
nums.lower(5); // 3 — largest element < 5Each returns null if no such element exists, instead of throwing.
NavigableSet: destructive reads and reverse order
javanums.pollFirst(); // removes and returns the least element, or null if empty
nums.pollLast(); // removes and returns the greatest element, or null if empty
nums.descendingSet(); // a NavigableSet view, greatest to least, backed by nums
nums.descendingIterator(); // an Iterator that walks greatest to leastNavigableSet range views with inclusive bounds
NavigableSet refines headSet/subSet/tailSet with an extra boolean per bound, controlling whether that boundary value itself is included:
javanums.headSet(5, true); // elements < 5, plus 5 itself if present
nums.subSet(3, true, 7, false); // elements >= 3 (incl.) and < 7 (excl.)
nums.tailSet(5, false); // elements > 5, excluding 5 itselfTrade-offs
Set.of() rejects duplicate arguments outright — unlike
List.of(), passing the same value twice doesn't silently deduplicate; it fails as soon as the set is built:javaSet<String> s = Set.of("a", "a"); // IllegalArgumentException: duplicate elementUnmodifiable sets throw at the call site, not silently — as with
List.of(), a mutator on aSet.of()result type-checks fine and only fails when it runs:javaSet<String> fixed = Set.of("a", "b"); fixed.add("c"); // UnsupportedOperationExceptionRange views are backed by the original set, in both directions —
headSet/subSet/tailSeton aSortedSetorNavigableSetshare storage with the set they came from, so mutating one is visible through the other:javaTreeSet<Integer> nums = new TreeSet<>(List.of(1, 3, 5, 7, 9)); SortedSet<Integer> view = nums.headSet(5); view.remove(3); System.out.println(nums); // [1, 5, 7, 9] — removal through the view affected numsOrdering assumes every element is mutually comparable, and nothing checks that up front — a
TreeSetaccepts anyObjectat compile time (or via aComparator<? super E>), so inserting an element that can't actually be compared to the others doesn't fail until an ordering operation forces the comparison, surfacing as aClassCastExceptionfromcompareTo()rather than fromSetitself.