Set Interface
java set interface HashSet LinkedHashSet TreeSet hashCode equals red-black tree unique elements
Introduction
In the previous lesson, you learned about the List Interface, where elements are stored in insertion order and duplicate values are allowed.
However, many real-world applications require unique values.
For example:
- Usernames must be unique.
- Email addresses should not be duplicated.
- Product IDs must be unique.
- Employee IDs cannot repeat.
- Tags in a blogging platform should appear only once.
- Unique visitors on a website.
Using a List allows duplicates:
List<String> users = new ArrayList<>();users.add("Rahul");users.add("Rahul");
Output:
RahulRahul
This is not desirable in many applications.
Java provides the Set Interface, which automatically prevents duplicate elements.
The Set interface is extensively used in:
- Spring Boot Applications
- Banking Systems
- E-Commerce Platforms
- Authentication Systems
- Search Engines
- Big Data Processing
- Caching Systems
In this lesson, you'll learn:
- What is Set?
- Characteristics of Set
- HashSet
- LinkedHashSet
- TreeSet
- Internal Architecture
- Hashing Mechanism
- Red-Black Tree
- Ordering
- Duplicate Detection
- Performance Analysis
- Memory Representation
- Best Practices
- Common Mistakes
- Hands-on Exercises
- Professional Interview Questions
What is Set?
A Set is a collection that stores unique elements.
Unlike List:
- No duplicate elements
- No index-based access
- Optimized for uniqueness
Example:
Set<String> fruits = new HashSet<>();fruits.add("Apple");fruits.add("Banana");fruits.add("Apple");System.out.println(fruits);
Possible Output:
AppleBanana
The duplicate "Apple" is ignored.
Characteristics of Set
A Set:
- Stores unique elements
- Allows one
nullelement (HashSet,LinkedHashSet) - Does not support indexes
- Supports iteration
- Optimized for membership checks
Set Hierarchy
Iterable│Collection│Set┌──────┼─────────────┐│ │ │HashSet LinkedHashSet TreeSet
Common Set Methods
add()remove()contains()size()isEmpty()clear()iterator()
Example:
Set<Integer> numbers =new HashSet<>();numbers.add(10);numbers.add(20);System.out.println(numbers.contains(20));
Output:
true
HashSet
HashSet is the most commonly used implementation of Set.
Internally, it uses a HashMap.
Example:
Set<String> cities =new HashSet<>();cities.add("Delhi");cities.add("Mumbai");cities.add("Hyderabad");
Internal Working of HashSet
Internally:
HashSet↓HashMap↓Hash Table↓Buckets↓Elements
Each element's hashCode() determines its bucket.
If multiple elements produce the same hash bucket, Java resolves collisions internally.
Hashing Mechanism
When adding an element:
set.add("Java");
Steps:
- Compute
hashCode() - Determine bucket index
- Compare using
equals() - If duplicate exists → Ignore
- Else → Store element
Pseudo Flow:
Object↓hashCode()↓Bucket↓equals()↓Store / Ignore
Why equals() and hashCode() Matter
Consider a custom class:
class Employee {int id;String name;}
If equals() and hashCode() are not overridden, two logically identical employees may both be stored in a HashSet.
Proper implementations ensure duplicate detection based on object content rather than memory references.
HashSet Memory Representation
Bucket 0↓JavaBucket 1↓SpringBucket 2↓Hibernate
Elements are distributed across buckets based on their hash values.
HashSet Time Complexity
| Operation | Average Complexity |
|---|---|
| add() | O(1) |
| remove() | O(1) |
| contains() | O(1) |
| iteration | O(n) |
These are average-case complexities. Performance may degrade if many hash collisions occur.
LinkedHashSet
LinkedHashSet extends HashSet by maintaining insertion order.
Example:
Set<String> skills =new LinkedHashSet<>();skills.add("Java");skills.add("Spring");skills.add("Kafka");System.out.println(skills);
Output:
JavaSpringKafka
Internally, it combines a hash table with a linked list.
LinkedHashSet Architecture
Hash Table↓Linked List↓Maintains Insertion Order
Use LinkedHashSet when uniqueness and predictable iteration order are both required.
TreeSet
TreeSet stores unique elements in sorted order.
Internally, it uses a Red-Black Tree.
Example:
Set<Integer> marks =new TreeSet<>();marks.add(80);marks.add(95);marks.add(70);System.out.println(marks);
Output:
708095
Red-Black Tree
A Red-Black Tree is a self-balancing binary search tree.
Benefits:
- Sorted data
- Balanced height
- Efficient insertions
- Efficient deletions
- Efficient searches
Approximate complexity:
Search↓Insert↓Delete↓O(log n)
HashSet vs LinkedHashSet vs TreeSet
| Feature | HashSet | LinkedHashSet | TreeSet |
|---|---|---|---|
| Duplicates | No | No | No |
| Ordering | No | Insertion Order | Sorted |
| Null Allowed | One | One | No |
| Internal Structure | Hash Table | Hash Table + Linked List | Red-Black Tree |
| Search | O(1) | O(1) | O(log n) |
TreeSet Ordering
Natural ordering:
TreeSet<String> languages =new TreeSet<>();languages.add("Java");languages.add("Python");languages.add("C");
Output:
CJavaPython
You can also provide a custom ordering using a Comparator.
Iterating Through a Set
Using Enhanced for Loop:
for(String city : cities){System.out.println(city);}
Using Iterator:
Iterator<String> iterator =cities.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}
Real-World Example: User Registration
Set<String> usernames =new HashSet<>();usernames.add("rahul");usernames.add("amit");usernames.add("rahul");System.out.println(usernames);
Duplicate usernames are automatically rejected.
Real-World Example: Website Tags
Set<String> tags =new LinkedHashSet<>();tags.add("Java");tags.add("Spring");tags.add("Microservices");
The insertion order is preserved while preventing duplicates.
Real-World Example: Leaderboard
TreeSet<Integer> scores =new TreeSet<>();scores.add(250);scores.add(180);scores.add(300);System.out.println(scores);
Output:
180250300
Best Practices
Override equals() and hashCode()
Whenever storing custom objects in a HashSet or LinkedHashSet, override both methods consistently.
Use HashSet by Default
Choose HashSet unless ordering is required.
Use LinkedHashSet for Predictable Iteration
If the order of insertion matters, prefer LinkedHashSet.
Use TreeSet for Sorted Data
When automatic sorting is required, use TreeSet.
Use Immutable Fields in Keys When Possible
Changing fields that participate in equals() or hashCode() after insertion can lead to incorrect behavior.
Common Mistakes
Expecting HashSet to Preserve Order
HashSet does not guarantee iteration order.
Forgetting equals() and hashCode()
Custom objects may appear duplicated if these methods are not implemented correctly.
Assuming TreeSet Allows Null
A TreeSet does not permit null elements because it relies on comparisons for ordering.
Modifying Objects After Insertion
Changing values that affect hashing or sorting can make elements difficult to locate.
Using TreeSet When Sorting Isn't Needed
TreeSet has higher overhead than HashSet. Use it only when sorted ordering is required.
Hands-on Exercise
Create a Java program that:
- Creates a
HashSetof employee IDs. - Demonstrates automatic duplicate removal.
- Creates a
LinkedHashSetof programming languages and verifies insertion order. - Creates a
TreeSetof exam scores and displays them in sorted order. - Creates a custom
Employeeclass and overridesequals()andhashCode(). - Uses
contains()to verify membership. - Iterates through each Set implementation using both enhanced
forloops andIterator.
Summary
The Set interface is designed for storing unique elements. HashSet offers excellent average-case performance, LinkedHashSet preserves insertion order, and TreeSet maintains sorted order using a Red-Black Tree. Choosing the correct implementation depends on whether uniqueness, ordering, or sorting is the primary requirement.
Key Takeaways
Setstores unique elements.HashSetuses hashing for fast average-case operations.LinkedHashSetpreserves insertion order.TreeSetstores elements in sorted order.TreeSetis backed by a Red-Black Tree.equals()andhashCode()are essential for custom objects in hash-based sets.TreeSetrelies on natural ordering or aComparator.- Choose the Set implementation based on ordering and performance requirements.
Professional Interview Questions
1What is the difference between HashSet, LinkedHashSet, and TreeSet?
Professional Answer
HashSet stores unique elements without guaranteeing iteration order and provides average-case O(1) performance for basic operations. LinkedHashSet extends HashSet by maintaining insertion order while retaining similar average-case performance. TreeSet stores unique elements in sorted order using a Red-Black Tree, with O(log n) complexity for insertion, deletion, and search.
Follow-up Questions
- Which Set implementation is the fastest for lookups?
- Which implementation preserves insertion order?
Interview Tip: Remember: HashSet → Fast, LinkedHashSet → Ordered, TreeSet → Sorted.
2Why must equals() and hashCode() be overridden for custom objects in a HashSet?
Professional Answer
HashSet uses hashCode() to determine the bucket where an object should be stored and equals() to determine whether two objects are logically equal. If these methods are not implemented consistently, logically identical objects may be stored as duplicates, violating the intended uniqueness of the Set.
Follow-up Questions
- What happens if only equals() is overridden?
- Why must equal objects return the same hash code?
Interview Tip: The contract is: If a.equals(b) is true, then a.hashCode() must equal b.hashCode().
3When should you use a TreeSet instead of a HashSet?
Professional Answer
Use a TreeSet when elements must remain automatically sorted or when range-based operations are required. Use a HashSet when ordering is not important and fast average-case lookup and insertion performance are the primary goals.
Follow-up Questions
- Can a TreeSet use a custom sorting strategy?
- Why doesn't TreeSet allow null elements?
Interview Tip: Choose based on the requirement: Need uniqueness only → HashSet; Need uniqueness + insertion order → LinkedHashSet; Need uniqueness + sorted order → TreeSet.