Learning outcome: Use sets to enforce uniqueness, apply HashSet operations, perform set algebra, and explain how equality and hashing determine duplicate detection.
A Set<E> stores unique elements. Adding an element that is already present does not create another copy. Sets are useful for registration IDs, visited pages, distinct tags, available permissions, and duplicate removal.
In simple terms: a set is a guest list. Writing the same name twice does not invite the person twice; the list simply stays as it was. There are no seat numbers either, so a set answers "is this person invited?" rather than "who is third?"
Set Properties
| Property | Meaning |
|---|---|
| Unique elements | Equal elements occur at most once |
| No index positions | Elements are accessed by iteration or membership tests |
| Collection operations | Sets inherit methods such as add, remove, and contains |
| Order depends on implementation | HashSet is unordered, LinkedHashSet preserves encounter order, and TreeSet sorts |
The add method reports whether the set changed.
Set<String> codes = new HashSet<>();
System.out.println(codes.add("CSE101")); // true
System.out.println(codes.add("CSE101")); // false
Useful trick: because add returns false when the element is already present, if (!seen.add(id)) { ... } is a one-line duplicate detector.
HashSet Characteristics
HashSet<E> is backed by a hash table and internally uses a HashMap to manage its elements.
| Characteristic | Meaning |
|---|---|
| Duplicate handling | Equal elements are ignored |
| Null support | One null element is permitted |
| Ordering | No iteration-order guarantee |
| Synchronisation | Not synchronized by default |
| Basic performance | add, remove, and contains are expected constant time with well-distributed hashes; size() is always constant time |
import java.util.HashSet;
import java.util.Set;
public class UniqueCourses {
public static void main(String[] args) {
Set<String> courses = new HashSet<>();
courses.add("Java");
courses.add("Networks");
courses.add("Java");
courses.add("Database");
System.out.println("Distinct count: " + courses.size());
System.out.println("Has Java: " + courses.contains("Java"));
}
}
Output:
Distinct count: 3
Has Java: true
The exact printed order of the set is not part of the contract.
List vs Set
| Requirement | List |
Set |
|---|---|---|
| Preserve repeated values | Yes | No |
| Access by index | Yes | No |
| Check uniqueness automatically | No | Yes |
| Typical use | Ordered sequence | Membership and distinct values |
Choose based on meaning, not only speed. If two equal entries represent two real events, use a list. If repetition represents invalid duplication, use a set.
In simple terms: two payments of the same amount are two real events, so that is a List. The same roll number appearing twice in an exam hall is a mistake, so that is a Set.
Removing Duplicates
Constructing a set from a collection removes duplicates.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RemoveDuplicates {
public static void main(String[] args) {
List<String> names = List.of("Aman", "Diya", "Aman", "Kabir", "Diya");
Set<String> unique = new HashSet<>(names);
List<String> result = new ArrayList<>(unique);
System.out.println("Distinct names: " + result.size());
}
}
Output:
Distinct names: 3
If the original encounter order must be preserved, use LinkedHashSet instead of HashSet.
Set Algebra
Bulk collection methods naturally express union, intersection, and difference.
In simple terms: picture two overlapping circles. addAll keeps everything in either circle (union), retainAll keeps only the overlap (intersection), and removeAll keeps the part of the first circle lying outside the second (difference). Each method changes the set it is called on, which is why the example copies each set first.
| Mathematical operation | Java operation |
|---|---|
| Union | addAll |
| Intersection | retainAll |
| Difference | removeAll |
import java.util.HashSet;
import java.util.Set;
public class SetOperations {
public static void main(String[] args) {
Set<String> javaClub = Set.of("Aman", "Diya", "Kabir");
Set<String> webClub = Set.of("Diya", "Kabir", "Meera");
Set<String> union = new HashSet<>(javaClub);
union.addAll(webClub);
Set<String> intersection = new HashSet<>(javaClub);
intersection.retainAll(webClub);
Set<String> onlyJava = new HashSet<>(javaClub);
onlyJava.removeAll(webClub);
System.out.println("Union size: " + union.size());
System.out.println("Both: " + intersection);
System.out.println("Only Java: " + onlyJava);
}
}
Sample output:
Union size: 4
Both: [Kabir, Diya]
Only Java: [Aman]
The sizes are deterministic, but the displayed order of each HashSet is not.
Equality and Hashing
add returns false.For built-in immutable types such as String and Integer, equality behaviour is already suitable. A custom class used in a set should implement equals and hashCode consistently. Otherwise, logically equal objects may be treated as different elements.
In simple terms: a set recognises a duplicate through hashCode() and equals(), not through the variable name or the memory address. If a custom class does not define them, two objects holding identical field values still count as different elements, and the "unique" set quietly fills with duplicates.
Do not modify fields involved in equality while an object is stored in a hash set. The object may move logically to a different hash bucket and become difficult to find or remove.
Choosing a Set Implementation
| Need | Implementation |
|---|---|
| Fast general membership testing, order unimportant | HashSet |
| Preserve encounter order | LinkedHashSet |
| Keep values sorted and support ranges | TreeSet |
Declare with the interface when implementation-specific methods are unnecessary.
Set<String> tags = new HashSet<>();
Common Mistakes
| Mistake | Correction |
|---|---|
Expecting add to create duplicates |
It returns false when an equal element already exists. |
Depending on HashSet iteration order |
Use LinkedHashSet or TreeSet when order matters. |
| Trying to access a set by index | Sets have no index-based get operation. |
| Using a custom class without suitable equality methods | Implement equals and hashCode consistently. |
| Editing equality fields after insertion | Prefer immutable set elements. |
Practice
- Read ten names and report how many distinct names were entered.
- Find the common skills between two students by using
retainAll. - Find students who registered for Java but not Web Development.
- Repeat duplicate removal with
LinkedHashSetand compare the order.
Quick Check
1. What does HashSet.add return for a duplicate element?
It returns false because the set does not change.
2. Does HashSet preserve insertion order?
No. It makes no guarantee about iteration order.
3. Which method computes the intersection of two mutable sets?
Copy one set and call retainAll with the other set.
4. Which two methods determine logical equality in a hash-based set?
hashCode() narrows the search and equals() confirms equality.
Lecture Quiz
A program must remove duplicates but preserve the first occurrence order. Which implementation should it use?
Use LinkedHashSet, which enforces uniqueness while preserving encounter order.
Summary
Set represents unique elements without index positions. HashSet provides efficient hash-based membership operations but does not guarantee order. Bulk methods express set algebra, and correct equals and hashCode implementations are essential for custom elements.