Set Interface in Java – Complete Guide with Examples
The Set interface represents a collection that cannot contain duplicate elements. If you add a value that is already present, the set simply ignores it. Set lives in java.util, extends Collection, and has been part of the Collections Framework since JDK 1.2.
Sets are the natural choice whenever uniqueness matters: unique visitors, unique tags, removing duplicates, or checking quickly whether something has been seen before. This tutorial covers the Set implementations, their ordering, set operations, and the equals()/hashCode() rules that make sets work.
1. What Is a Set?
Think of a guest list at a party. Each name appears once; writing a name a second time does not add a second guest. Java's Set follows the same rule, modelled on the mathematical idea of a set.
- No duplicates: two elements
e1ande2withe1.equals(e2)cannot both be in the set. - No indexes: there is no
get(i); you loop over a set or check membership withcontains(). - Order depends on the class: none, insertion order, or sorted order.
- Fast lookups:
HashSet.contains()runs in constant time on average. - At most one
nullinHashSetandLinkedHashSet; none inTreeSetorSet.of().
2. Hierarchy and Declaration
public interface Set<E> extends Collection<E>
The sub-interfaces SortedSet and NavigableSet add sorted-order features and are implemented by TreeSet. Java 21 added SequencedSet, implemented by LinkedHashSet and the sorted sets.
3. Using a Set: The Basics
A set uses the methods of Collection. The interesting one is add(): it returns false when the element is already present.
import java.util.HashSet;
import java.util.Set;
public class SetBasics {
public static void main(String[] args) {
Set<String> visitors = new HashSet<>();
System.out.println("add(Asha): " + visitors.add("Asha"));
System.out.println("add(Ravi): " + visitors.add("Ravi"));
System.out.println("add(Asha): " + visitors.add("Asha")); // duplicate ignored
System.out.println("Visitors: " + visitors);
System.out.println("Count: " + visitors.size());
System.out.println("Has Ravi? " + visitors.contains("Ravi"));
visitors.remove("Ravi");
System.out.println("After remove: " + visitors);
}
}
add(Asha): true add(Ravi): true add(Asha): false Visitors: [Asha, Ravi] Count: 2 Has Ravi? true After remove: [Asha]
4. Set Implementations
The three everyday implementations differ mainly in the order in which they return elements:
import java.util.*;
public class SetImplementations {
public static void main(String[] args) {
List<Integer> input = List.of(42, 7, 19, 7, 3, 42, 25);
Set<Integer> hash = new HashSet<>(input);
Set<Integer> linked = new LinkedHashSet<>(input);
Set<Integer> tree = new TreeSet<>(input);
System.out.println("Input: " + input);
System.out.println("HashSet: " + hash + " (no guaranteed order)");
System.out.println("LinkedHashSet: " + linked + " (insertion order)");
System.out.println("TreeSet: " + tree + " (sorted)");
}
}
Input: [42, 7, 19, 7, 3, 42, 25] HashSet: [19, 3, 7, 25, 42] (no guaranteed order) LinkedHashSet: [42, 7, 19, 3, 25] (insertion order) TreeSet: [3, 7, 19, 25, 42] (sorted)
| Class | Internal structure | Order | add / contains / remove | null |
|---|---|---|---|---|
| HashSet | Hash table (a HashMap) | No guaranteed order | O(1) | One allowed |
| LinkedHashSet | Hash table + linked list | Insertion order | O(1) | One allowed |
| TreeSet | Red-black tree (a TreeMap) | Sorted | O(log n) | Not allowed |
EnumSet | Bit vector | Enum declaration order | O(1), very fast | Not allowed |
CopyOnWriteArraySet | Array copied on write | Insertion order | O(n) | Allowed |
ConcurrentSkipListSet | Skip list | Sorted | O(log n), thread-safe | Not allowed |
5. Set Operations: Union, Intersection, Difference
Sets make the classic set operations easy. Each bulk method changes the set it is called on, so copy the original first.
import java.util.Set;
import java.util.TreeSet;
public class SetOperations {
public static void main(String[] args) {
Set<String> java = Set.of("Asha", "Ravi", "Meera", "John");
Set<String> python = Set.of("Ravi", "John", "Kiran");
Set<String> union = new TreeSet<>(java);
union.addAll(python);
System.out.println("Learn Java or Python: " + union);
Set<String> both = new TreeSet<>(java);
both.retainAll(python);
System.out.println("Learn both: " + both);
Set<String> onlyJava = new TreeSet<>(java);
onlyJava.removeAll(python);
System.out.println("Learn only Java: " + onlyJava);
System.out.println("Is {Ravi, John} a subset of Java? " + java.containsAll(Set.of("Ravi", "John")));
}
}
Learn Java or Python: [Asha, John, Kiran, Meera, Ravi]
Learn both: [John, Ravi]
Learn only Java: [Asha, Meera]
Is {Ravi, John} a subset of Java? true
| Operation | Code |
|---|---|
| Union (A ∪ B) | a.addAll(b) |
| Intersection (A ∩ B) | a.retainAll(b) |
| Difference (A − B) | a.removeAll(b) |
| Subset (B ⊆ A) | a.containsAll(b) |
6. equals() and hashCode(): How a Set Finds Duplicates
A HashSet uses an element's hashCode() to pick a bucket, then equals() to compare with the elements already in that bucket. If your class does not override both methods, two objects with the same data are treated as different, and the set ends up with "duplicates".
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class CustomObjectsInSet {
// No equals()/hashCode(): every object is "different"
static class PointNoEquals {
final int x, y;
PointNoEquals(int x, int y) { this.x = x; this.y = y; }
}
// equals() and hashCode() based on the fields
static class Point {
final int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override public boolean equals(Object o) {
return o instanceof Point p && p.x == x && p.y == y;
}
@Override public int hashCode() { return Objects.hash(x, y); }
}
// A record gets equals() and hashCode() automatically
record Pixel(int x, int y) { }
public static void main(String[] args) {
Set<PointNoEquals> a = new HashSet<>();
a.add(new PointNoEquals(1, 2));
a.add(new PointNoEquals(1, 2));
System.out.println("Without equals/hashCode: size = " + a.size());
Set<Point> b = new HashSet<>();
b.add(new Point(1, 2));
b.add(new Point(1, 2));
System.out.println("With equals/hashCode: size = " + b.size());
Set<Pixel> c = new HashSet<>();
c.add(new Pixel(1, 2));
c.add(new Pixel(1, 2));
System.out.println("With a record: size = " + c.size());
}
}
Without equals/hashCode: size = 2 With equals/hashCode: size = 1 With a record: size = 1
- If two objects are equal, they must have the same hash code.
- Use the same fields in both methods, and prefer immutable fields.
- Never change an element's fields while it is in a set. Its hash code would change, and the set would no longer find it.
TreeSetusescompareTo()or aComparatorinstead; elements that compare as 0 are treated as duplicates.
7. Immutable Sets with Set.of()
Java 9 added Set.of() for small, fixed sets, and Java 10 added Set.copyOf(). Both create unmodifiable sets that reject null.
import java.util.*;
public class ImmutableSets {
public static void main(String[] args) {
Set<String> roles = Set.of("ADMIN", "USER");
try {
roles.add("GUEST");
} catch (UnsupportedOperationException e) {
System.out.println("Set.of() is unmodifiable");
}
try {
Set.of("A", "A");
} catch (IllegalArgumentException e) {
System.out.println("Set.of() rejects duplicates: " + e.getMessage());
}
List<String> withDupes = List.of("x", "y", "x");
Set<String> copy = Set.copyOf(withDupes); // duplicates are dropped here
System.out.println("Set.copyOf() size: " + copy.size());
}
}
Set.of() is unmodifiable Set.of() rejects duplicates: duplicate element: A Set.copyOf() size: 2
Notice the difference: Set.of() treats duplicate arguments as a programming mistake and throws, while Set.copyOf() quietly drops duplicates from the source collection. The iteration order of these sets is unspecified and may differ between runs.
8. Removing and Finding Duplicates
Two of the most common uses of a set: turning a list into unique values, and finding which values repeat.
import java.util.*;
public class RemoveDuplicatesWithSet {
public static void main(String[] args) {
List<String> emails = List.of("a@x.com", "b@x.com", "a@x.com", "c@x.com", "b@x.com");
Set<String> unique = new LinkedHashSet<>(emails); // keeps first-seen order
System.out.println("Unique emails: " + unique);
// Find the duplicates: add() returns false the second time
Set<String> seen = new HashSet<>();
Set<String> duplicates = new TreeSet<>();
for (String e : emails) {
if (!seen.add(e)) duplicates.add(e);
}
System.out.println("Duplicates: " + duplicates);
}
}
Unique emails: [a@x.com, b@x.com, c@x.com] Duplicates: [a@x.com, b@x.com]
9. Methods of the Set Interface
Set adds no new abstract methods to Collection; it tightens their meaning so that duplicates are never stored. It also adds static factory methods.
| Method | Description |
|---|---|
add(e) | Adds the element if it is not already present; returns false otherwise. |
addAll(c) | Adds every element of c that is not present (union). |
remove(o) | Removes the element if present. |
removeAll(c), retainAll(c) | Difference and intersection. |
contains(o), containsAll(c) | Membership and subset checks. |
size(), isEmpty(), clear() | Count, check and empty. |
iterator(), forEach(), stream() | Visit the elements. |
equals(o), hashCode() | Two sets are equal if they contain the same elements, in any order. |
Set.of(...), Set.copyOf(c) | Create unmodifiable sets (Java 9 and 10). |
10. Which Set Should You Use?
- HashSet: you only need uniqueness and fast lookups. This is the default.
- LinkedHashSet: you also want elements back in the order they were added.
- TreeSet: you want elements sorted, or need "smallest", "largest" or "nearest" queries.
- EnumSet: the elements are enum constants.
- ConcurrentHashMap.newKeySet() or ConcurrentSkipListSet: several threads share the set.
- Set.of(): a small fixed set of constants.
11. Set vs List
| Feature | Set | List |
|---|---|---|
| Duplicates | Not allowed | Allowed |
| Access by index | No | Yes |
| Order | Depends on the class | Insertion order |
contains() | O(1) for HashSet | O(n) |
| Equality | Same elements, any order | Same elements, same order |
12. Common Mistakes
- Forgetting
equals()andhashCode()in your own classes, so "duplicates" appear. - Changing an element after adding it, so
contains()andremove()no longer find it. - Relying on HashSet order; use LinkedHashSet or TreeSet when order matters.
- Adding
nullto a TreeSet, which throwsNullPointerException. - Calling
add()onSet.of(), which throwsUnsupportedOperationException. - Removing inside a for-each loop; use
removeIf()or an iterator.
13. Summary
Setis a collection without duplicates and without indexes.HashSethas no order,LinkedHashSetkeeps insertion order,TreeSetkeeps elements sorted.- Hash-based sets rely on
equals()andhashCode(); tree sets rely on comparison. addAll,retainAllandremoveAllgive union, intersection and difference.Set.of()andSet.copyOf()create unmodifiable sets.
Related Tutorials
Reference: Java SE 21 API: java.util.Set
Frequently Asked Questions (FAQ)
Q1. What is the Set interface in Java?
Set is an interface in java.util for collections that contain no duplicate elements. It extends Collection and is implemented by classes such as HashSet, LinkedHashSet and TreeSet.
Q2. What happens if I add a duplicate element to a Set?
Nothing is added and add() returns false. No exception is thrown (except by Set.of(), which rejects duplicate arguments with IllegalArgumentException).
Q3. How does a Set decide that two elements are duplicates?
Hash-based sets use hashCode() and equals(). Sorted sets use compareTo() or the supplied Comparator; if it returns 0, the elements are treated as duplicates.
Q4. What is the difference between HashSet, LinkedHashSet and TreeSet?
HashSet has no guaranteed order and O(1) operations. LinkedHashSet keeps insertion order, also O(1). TreeSet keeps elements sorted, with O(log n) operations, and offers navigation methods such as first(), floor() and ceiling().
Q5. Does a Set allow null?
HashSet and LinkedHashSet allow one null. TreeSet (with natural ordering), EnumSet, ConcurrentSkipListSet and Set.of() do not.
Q6. Can I get an element from a Set by index?
No. Sets have no positions. Loop over the set, use a stream, or copy it into a list (new ArrayList<>(set)) if you need indexes. A TreeSet offers first() and last(), and from Java 21 sequenced sets offer getFirst() and getLast().
Q7. Is a Set ordered?
It depends on the implementation. HashSet is unordered, LinkedHashSet preserves insertion order, and TreeSet is sorted.
Q8. How do I remove duplicates from a list using a Set?
List<String> unique = new ArrayList<>(new LinkedHashSet<>(list));
LinkedHashSet keeps the first occurrence of each value in its original order. Use a TreeSet if you want the result sorted.
Q9. Why do I get duplicates of my own objects in a HashSet?
Your class probably does not override equals() and hashCode(), so Java compares object identity instead of data. Override both using the same fields, or use a record, which generates them for you.
Q10. What happens if I modify an object after adding it to a HashSet?
If the change affects hashCode(), the object stays in its old bucket. contains() and remove() will then fail to find it, and you may be able to add an "equal" copy. Keep set elements immutable.
Q11. How do I find the intersection of two sets?
Set<String> common = new HashSet<>(a);
common.retainAll(b);
Copy first, because retainAll() modifies the set it is called on.
Q12. When are two sets equal?
When they contain exactly the same elements, regardless of order or implementation class. A HashSet and a TreeSet with the same elements are equal.
Q13. What is the difference between Set.of() and new HashSet<>()?
Set.of() creates an unmodifiable set that rejects null and duplicate arguments, with an unspecified iteration order. new HashSet<>() creates a normal modifiable set.
Q14. Is HashSet thread-safe?
No. For a shared set, use ConcurrentHashMap.newKeySet(), ConcurrentSkipListSet (sorted), CopyOnWriteArraySet (mostly reads), or Collections.synchronizedSet(new HashSet<>()).
Q15. How is HashSet implemented internally?
A HashSet wraps a HashMap. Each element is stored as a key, and a shared dummy object is stored as the value. That is why it has the same O(1) performance and null rules as HashMap keys.
Q16. What is EnumSet?
A specialised set for enum constants, created with methods such as EnumSet.of(Day.MON, Day.FRI) or EnumSet.allOf(Day.class). It stores elements as bits, so it is extremely fast and compact, and it iterates in declaration order.
Q17. How do I sort a Set?
Copy it into a TreeSet (new TreeSet<>(set)), or into a list and sort the list. You can pass a Comparator to the TreeSet constructor for a custom order.
Q18. Which is faster for contains(): List or Set?
A HashSet, by far, for large collections: O(1) on average compared with O(n) for a list. TreeSet is O(log n). If you check membership often, convert the list to a set first.
Q19. Can a Set contain another Set?
Yes, for example Set<Set<Integer>>. Use immutable inner sets, such as those from Set.of(), because changing an inner set after adding it changes its hash code.
Q20. What is a SortedSet and a NavigableSet?
SortedSet keeps elements sorted and adds first(), last(), headSet(), tailSet() and subSet(). NavigableSet extends it with lower(), floor(), ceiling(), higher(), pollFirst(), pollLast() and descendingSet(). TreeSet implements both.