Set
In short: A collections interface for collections with no duplicates — every element can be contained at most once.
In more detail: Adding an already-present element again doesn’t change the Set (no error, but also no second entry). The concrete implementations HashSet (no guaranteed order, very fast) and TreeSet (sorted, somewhat slower) differ mainly in how they organise elements internally.
In Depth
Set<String> names = new HashSet<>();
names.add("Anna");
names.add("Bob");
names.add("Anna"); // ignored, no duplicate
System.out.println(names.size()); // 2
if (names.contains("Anna")) {
System.out.println("Anna is already in there");
}
names.remove("Bob");Whether an element counts as “equal” isn’t decided by == (object identity), but by the stored class’s equals()/hashCode() implementation. For custom classes, both methods therefore have to be overridden consistently — otherwise two objects that are identical in content still end up duplicated in the Set, because HashSet classifies them as different via different hashCode() values. Set itself is only an interface (part of Collections); which concrete implementation to choose depends on the need:
- HashSet: fastest access (O(1) on average), no guaranteed order.
- TreeSet: always sorted, but slower (O(log n)).
LinkedHashSet: preserves insertion order, a compromise between the two.
Typical use case: removing duplicates from a list by simply converting it into a Set (new HashSet<>(list)), or quickly checking whether a value has already occurred, without linearly searching the entire collection as with a List.
Set operations — union, intersection, difference
Set maps the mathematical set operations directly onto collection methods that change the calling set itself:
Set<Integer> a = new HashSet<>(Set.of(1, 2, 3, 4));
Set<Integer> b = new HashSet<>(Set.of(3, 4, 5, 6));
Set<Integer> union = new HashSet<>(a);
union.addAll(b); // {1, 2, 3, 4, 5, 6}
Set<Integer> intersection = new HashSet<>(a);
intersection.retainAll(b); // only elements in BOTH: {3, 4}
Set<Integer> difference = new HashSet<>(a);
difference.removeAll(b); // only in a, not in b: {1, 2}Since addAll()/retainAll()/removeAll() directly change the calling set, a copy (new HashSet<>(a)) is deliberately created first here, to leave the original untouched.
Why equals()/hashCode() are crucial for custom classes
Without overridden equals()/hashCode(), Java by default compares objects by reference (memory address) — two objects that are identical in content but created separately then count as “different” and both end up in the Set:
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
// WITHOUT equals()/hashCode(): two Point(1,1) objects count as different!
}
Set<Point> points = new HashSet<>();
points.add(new Point(1, 1));
points.add(new Point(1, 1)); // NOT recognized as a duplicate without equals()/hashCode()
System.out.println(points.size()); // 2, not 1!See also: HashSet, TreeSet, Collections