Collections Framework
Sets
Set rejects duplicates as defined by equals (or compareTo in sorted sets). HashSet is O(1) and unordered, LinkedHashSet remembers insertion order, TreeSet iterates sorted at O(log n) per operation.HashSet: hash table over a backingHashMap; iteration order is arbitrary and can changeLinkedHashSet: insertion-ordered iteration for a small memory premiumTreeSet: red-black tree, sorted iteration,NavigableSetrange queries- Elements must have consistent
equals/hashCode— and stay unmutated while inside EnumSetfor enum elements: bit-vector speed with Set semantics- Iteration order:
HashSetarbitrary,LinkedHashSetinsertion order,TreeSetsorted order Set.of(...)rejects duplicate arguments at construction — throwsIllegalArgumentException, not a silent dedup
A Set is best understood as a Map with no values: HashSet is a HashMap<E, Object> sharing one dummy value, and TreeSet wraps a TreeMap. Everything that governs a map — how equals/hashCode decide identity, iteration order, load factor (Hashing Internals) — governs the matching set verbatim. Pick a set the same way you'd pick its map: HashSet for O(1) membership and don't-care order, LinkedHashSet to remember insertion order, TreeSet when you need sorted iteration or range queries.
Set<String> seen = new HashSet<>();
for (String word : words) {
if (!seen.add(word)) { // add() returns false for duplicates
System.out.println("dup: " + word);
}
}
Set<String> sorted = new TreeSet<>(words); // sorted, deduped
Set<String> ordered = new LinkedHashSet<>(words); // first-seen order keptSet algebra comes from bulk operations: a.retainAll(b) (intersection), a.addAll(b) (union), a.removeAll(b) (difference). For membership-heavy pipelines, Set.contains at O(1) beats List.contains at O(n) — a frequent silent performance bug (Choosing Collections).
TreeSet implements NavigableSet: first(), last(), floor(e), ceiling(e), headSet, tailSet, subSet — range views over the sorted order. Remember membership is decided by compareTo, not equals (Sorted Collections).
record LogEntry(String host, Instant at) {}
Set<String> distinctHosts = entries.stream()
.map(LogEntry::host)
.collect(Collectors.toCollection(HashSet::new)); // membership only, order irrelevant
NavigableSet<Integer> ports = new TreeSet<>(Set.of(22, 80, 443, 8080));
ports.ceiling(100); // 443 — smallest element >= 100
ports.headSet(443); // [22, 80] — everything below 443
ports.descendingSet(); // 8080, 443, 80, 22
// ports.contains(443) is O(log n) here; a HashSet would answer the same question in O(1)
// — pay for navigation only when you actually use floor/ceiling/range queries