Understanding Set, HashSet, and LinkedHashSet in Java
Course: Core Java Training
Topic: Understanding Set, HashSet, and LinkedHashSet
Course Link: Core Java Training Course
Demo Link: Register for a Free Course Demo
Official Website: JustAcademy
1. Introduction to Set in Java
The Java Collections Framework provides several interfaces and classes for storing and managing groups of objects. The Set interface is one of the most important collection interfaces because it stores unique elements and prevents duplicate values.
Unlike a List, which allows duplicate elements and generally maintains positional order, a Set focuses on uniqueness. The actual ordering of elements depends on the implementation used.
For example, an application may receive the following product categories:
Electronics
Books
Clothing
Books
Electronics
A Set can store these values without duplicates:
Electronics
Books
Clothing
Java provides several implementations of Set, including HashSet, LinkedHashSet, and TreeSet. This topic focuses on Set, HashSet, and LinkedHashSet.
2. Learning Objectives
- Understand the purpose and characteristics of the Set interface.
- Learn how HashSet and LinkedHashSet work.
- Create, add, search, and remove elements from sets.
- Understand duplicate handling and null values.
- Compare HashSet and LinkedHashSet.
- Use set operations such as union, intersection, and difference.
- Apply sets to practical Java programming problems.
3. What Is the Set Interface?
Set is an interface in the java.util package. It extends the Collection interface and represents a collection that does not allow duplicate elements.
Because Set is an interface, you cannot directly create an instance using new Set<>(). Instead, you create an object of a class that implements Set, such as HashSet or LinkedHashSet.
Basic Syntax
import java.util.Set;
import java.util.HashSet;
public class SetExample {
public static void main(String[] args) {
Set<String> languages = new HashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("Java");
System.out.println(languages);
}
}
Possible output:
[Java, Python]
The output contains only one occurrence of Java. The element order is not guaranteed for HashSet, so the displayed order may vary.
4. Important Characteristics of Set
- No duplicates: A Set does not store duplicate elements according to its equality rules.
- No index-based access: The Set interface does not provide methods such as
get(0).
- Ordering depends on implementation: HashSet does not guarantee iteration order, while LinkedHashSet preserves insertion order.
- Generic support: You can use generics to specify the type of elements.
- Collection operations: Sets support methods such as
add(), remove(), contains(), and size().
- Null support varies by implementation: HashSet and LinkedHashSet permit a null element.
5. Understanding HashSet
HashSet is a class in the java.util package that implements the Set interface. It uses a hash-table-based implementation to store unique elements and offers efficient basic operations when hash values are distributed properly.
Key Features of HashSet
- Does not allow duplicate elements.
- Does not guarantee insertion order or sorted order.
- Permits one null element.
- Provides typically constant-time performance for adding, removing, and checking elements when hashing works well.
- Is not synchronized by default.
Creating a HashSet
import java.util.HashSet;
import java.util.Set;
public class HashSetCreation {
public static void main(String[] args) {
Set<String> fruits = new HashSet<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Mango");
System.out.println(fruits);
}
}
Note: The elements may be displayed in a different order because HashSet does not promise a particular iteration order.
6. Understanding LinkedHashSet
LinkedHashSet is a Set implementation that combines hash-table-based storage with a linked list that maintains encounter order. In ordinary use, this means elements appear during iteration in the order they were first inserted.
Key Features of LinkedHashSet
- Prevents duplicate elements.
- Preserves insertion order during iteration.
- Permits one null element.
- Offers efficient basic operations, typically close to HashSet, with some extra memory and maintenance overhead.
- Is not synchronized by default.
Creating a LinkedHashSet
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetExample {
public static void main(String[] args) {
Set<String> cities = new LinkedHashSet<>();
cities.add("Mumbai");
cities.add("Pune");
cities.add("Delhi");
cities.add("Mumbai");
System.out.println(cities);
}
}
Output:
[Mumbai, Pune, Delhi]
Mumbai is displayed once, and the original insertion order of the unique elements is preserved.
7. Difference Between Set, HashSet, and LinkedHashSet
| Feature |
Set |
HashSet |
LinkedHashSet |
| Type |
Interface |
Class |
Class |
| Purpose |
Defines a unique-element collection |
Stores unique elements efficiently |
Stores unique elements while preserving insertion order |
| Duplicates |
Not allowed |
Not allowed |
Not allowed |
| Insertion order |
Depends on implementation |
Not guaranteed |
Preserved during iteration |
| Sorted order |
Not guaranteed by the interface |
No |
No |
| Null element |
Depends on implementation |
Allows one |
Allows one |
| Performance |
Depends on implementation |
Typically constant-time basic operations with good hashing |
Typically constant-time basic operations with good hashing |
| Memory usage |
Depends on implementation |
Generally lower than LinkedHashSet for comparable usage |
Extra memory for maintaining insertion order |
| Best use |
Programming against a collection contract |
Uniqueness when order is unimportant |
Uniqueness when insertion order matters |
8. Adding Elements Using add()
The add() method inserts an element into a set if it is not already present. It returns true if the set changes and false if the element was already present.
import java.util.HashSet;
import java.util.Set;
public class AddElements {
public static void main(String[] args) {
Set<Integer> numbers = new HashSet<>();
System.out.println(numbers.add(10));
System.out.println(numbers.add(20));
System.out.println(numbers.add(10));
System.out.println(numbers);
}
}
Output:
true
true
false
[20, 10]
The exact order in the final line may differ. The third call returns false because 10 is already present.
9. Adding Multiple Elements Using addAll()
The addAll() method adds all elements from another collection. Duplicate values are ignored according to the Set's equality rules.
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class AddAllExample {
public static void main(String[] args) {
Set<String> first = new HashSet<>();
first.add("Java");
first.add("Python");
Set<String> second =
new HashSet<>(Arrays.asList("Python", "C++", "JavaScript"));
first.addAll(second);
System.out.println(first);
}
}
The resulting set contains Java, Python, C++, and JavaScript, each only once. Its display order is not guaranteed.
10. Checking Elements with contains()
The contains() method checks whether a set includes a particular element. It returns a boolean value.
import java.util.HashSet;
import java.util.Set;
public class ContainsExample {
public static void main(String[] args) {
Set<String> courses = new HashSet<>();
courses.add("Core Java");
courses.add("SQL");
courses.add("Python");
System.out.println(courses.contains("SQL"));
System.out.println(courses.contains("JavaScript"));
}
}
Output:
true
false
This method is useful when checking whether a username, product code, category, or other value has already been registered.
11. Removing Elements from a Set
The remove() method removes a matching element if it exists. It returns true when the set changes as a result of the operation.
import java.util.LinkedHashSet;
import java.util.Set;
public class RemoveExample {
public static void main(String[] args) {
Set<String> subjects = new LinkedHashSet<>();
subjects.add("Java");
subjects.add("SQL");
subjects.add("HTML");
subjects.remove("SQL");
System.out.println(subjects);
}
}
Output:
[Java, HTML]
Other Removal Methods
remove(Object value) removes a specified element.
removeAll(Collection<?> values) removes elements found in another collection.
retainAll(Collection<?> values) keeps only elements also found in another collection.
clear() removes all elements.
12. Finding the Size and Checking Whether a Set Is Empty
The size() method returns the number of unique elements. The isEmpty() method checks whether the set contains any elements.
Set<Integer> ids = new HashSet<>();
ids.add(101);
ids.add(102);
ids.add(101);
System.out.println(ids.size()); // 2
System.out.println(ids.isEmpty()); // false
ids.clear();
System.out.println(ids.isEmpty()); // true
13. Iterating Through a Set
A Set does not provide index-based access like a List. You can traverse its elements using an enhanced for loop, an iterator, or the forEach() method.
Using an Enhanced For Loop
Set<String> tools = new LinkedHashSet<>();
tools.add("Eclipse");
tools.add("IntelliJ IDEA");
tools.add("VS Code");
for (String tool : tools) {
System.out.println(tool);
}
Output:
Eclipse
IntelliJ IDEA
VS Code
Using an Iterator
import java.util.Iterator;
Iterator<String> iterator = tools.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
Using forEach()
tools.forEach(tool -> System.out.println(tool));
When iteration order matters, use an implementation with a defined order, such as LinkedHashSet. HashSet does not guarantee iteration order.
14. How Does HashSet Prevent Duplicates?
HashSet uses hashing to locate elements and uses equality checks to determine whether an equivalent element is already present. The hashCode() method helps locate a candidate storage area, while equals() determines whether objects are logically equal.
For example, adding the same String twice does not create two entries:
Set<String> names = new HashSet<>();
names.add("Amit");
names.add("Amit");
System.out.println(names.size()); // 1
Important Rule for Custom Objects
If you store custom objects in a HashSet or LinkedHashSet, implement equals() and hashCode() consistently. Objects considered equal must return the same hash code.
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
class Student {
private final int id;
private final String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Student)) return false;
Student other = (Student) obj;
return id == other.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return id + " - " + name;
}
}
public class StudentSetExample {
public static void main(String[] args) {
Set<Student> students = new HashSet<>();
students.add(new Student(1, "Amit"));
students.add(new Student(1, "Amit Sharma"));
students.add(new Student(2, "Neha"));
System.out.println(students.size()); // 2
}
}
In this example, student identity is defined by the ID. Therefore, two Student objects with the same ID are considered equal, even if their names differ.
15. Handling Null Values
HashSet and LinkedHashSet both permit a single null element. Repeated attempts to add null do not create additional null entries.
Set<String> values = new LinkedHashSet<>();
values.add("Java");
values.add(null);
values.add(null);
System.out.println(values); // [Java, null]
System.out.println(values.size()); // 2
Although null is allowed by these implementations, consider whether it is meaningful in your application. Unexpected null values can make validation and business logic harder to understand.
16. Removing Duplicates from a List Using HashSet
A common use of Set is to remove duplicate values from a List. Creating a HashSet from a List keeps only unique values, but the original list order is not guaranteed in the resulting HashSet.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RemoveDuplicates {
public static void main(String[] args) {
List<String> items = new ArrayList<>(
Arrays.asList("Java", "SQL", "Java", "Python", "SQL")
);
Set<String> uniqueItems = new HashSet<>(items);
System.out.println(uniqueItems);
}
}
Preserving Original Insertion Order
If you want to remove duplicates while retaining the order in which values first appeared, use LinkedHashSet.
Set<String> uniqueItems = new LinkedHashSet<>(items);
System.out.println(uniqueItems);
// [Java, SQL, Python]
17. Performing Set Operations
Set operations are useful for comparing groups of data, such as enrolled students, registered users, selected categories, or available products.
Union
The union contains all unique elements from both sets. Use addAll().
Set<Integer> a = new HashSet<>();
a.add(1);
a.add(2);
a.add(3);
Set<Integer> b = new HashSet<>();
b.add(3);
b.add(4);
b.add(5);
Set<Integer> union = new HashSet<>(a);
union.addAll(b);
System.out.println(union);
Expected elements: 1, 2, 3, 4, 5. Their printed order is not guaranteed.
Intersection
The intersection contains only elements shared by both sets. Use retainAll().
Set<Integer> intersection = new HashSet<>(a);
intersection.retainAll(b);
System.out.println(intersection); // [3]
Difference
The difference contains elements in the first set that are not present in the second set. Use removeAll().
Set<Integer> difference = new HashSet<>(a);
difference.removeAll(b);
System.out.println(difference); // [1, 2]
These operations modify the target set. Creating a copy first helps preserve the original sets.
18. Converting a Set into an Array or List
You may need to convert a Set when another method requires a List or an array.
Set to Array
Set<String> languages = new LinkedHashSet<>();
languages.add("Java");
languages.add("Python");
String[] array = languages.toArray(new String[0]);
for (String language : array) {
System.out.println(language);
}
Set to List
import java.util.ArrayList;
import java.util.List;
List<String> languageList = new ArrayList<>(languages);
System.out.println(languageList);
Converting from HashSet does not create a guaranteed meaningful order. Use LinkedHashSet first if preserving insertion order is important.
19. Sorting Set Elements
HashSet and LinkedHashSet do not automatically sort their elements. If sorted order is required, you can copy the values into a list and sort them, or use a TreeSet when its ordering rules fit your needs.
Sorting by Copying into a List
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
Set<Integer> numbers = new HashSet<>();
numbers.add(40);
numbers.add(10);
numbers.add(30);
numbers.add(20);
List<Integer> sortedNumbers = new ArrayList<>(numbers);
Collections.sort(sortedNumbers);
System.out.println(sortedNumbers); // [10, 20, 30, 40]
20. Performance and Internal Working
HashSet and LinkedHashSet rely on hashing for locating elements. Their basic operations are typically close to constant time when hash values are distributed well. Performance can degrade if hashing is poor or many elements collide.
| Operation |
Typical performance |
Notes |
add() |
Average O(1) |
May involve resizing or collision handling. |
contains() |
Average O(1) |
Depends on hashing and equality checks. |
remove() |
Average O(1) |
Depends on hashing and collision handling. |
| Full iteration |
Depends on implementation and capacity |
LinkedHashSet iteration is proportional to the number of elements; HashSet iteration can depend on both size and table capacity. |
These are typical performance characteristics, not guarantees for every workload. Choose a collection based on the data and ordering requirements of your application.
21. Choosing Between HashSet and LinkedHashSet
Use HashSet When
- You need to store unique elements.
- You do not need a predictable iteration order.
- You want a general-purpose set with efficient basic operations.
Use LinkedHashSet When
- You need unique elements and predictable insertion order.
- You want to remove duplicates while preserving first-seen order.
- You need to return results in the same order in which they were supplied.
Example: Choosing the Correct Implementation
// Order is unimportant
Set<String> uniqueCodes = new HashSet<>();
// Insertion order is important
Set<String> orderedCodes = new LinkedHashSet<>();
22. Common Errors and Troubleshooting
Error 1: Expecting HashSet to Preserve Insertion Order
Problem: The output does not appear in the order in which values were added.
Reason: HashSet does not guarantee iteration order.
Solution: Use LinkedHashSet when insertion order matters.
Error 2: Using Index-Based Access
Problem: Trying to call set.get(0).
Reason: The Set interface does not provide indexed access.
Solution: Use iteration, or convert the set to a List when positional access is required.
Error 3: Custom Objects Are Not Being Treated as Duplicates
Problem: Two objects with the same field values both appear in a HashSet.
Reason: The class may rely on default object equality rather than the intended logical identity.
Solution: Implement equals() and hashCode() consistently.
Error 4: Changing Fields Used in Hashing
Problem: An object becomes difficult to find or remove after it is inserted into a set.
Reason: A field used by equals() or hashCode() changed after insertion.
Solution: Prefer immutable fields for equality and hashing, or avoid modifying those fields while an object is stored in the set.
Error 5: Concurrent Modification
Problem: A ConcurrentModificationException occurs while iterating and modifying a set.
Reason: The collection was structurally modified outside the iterator while iteration was in progress.
Solution: Use the iterator's own remove() method when removing during iterator traversal, or use an appropriate alternative approach. Fail-fast behavior is best-effort and is not a thread-safety mechanism.
23. Thread Safety Considerations
HashSet and LinkedHashSet are not synchronized by default. If multiple threads access the same set and at least one thread modifies it, you must provide suitable synchronization or use an appropriate concurrent collection.
For simple synchronized access, Java provides Collections.synchronizedSet():
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
Set<String> sharedSet = Collections.synchronizedSet(
new LinkedHashSet<>()
);
sharedSet.add("Java");
sharedSet.add("SQL");
When iterating over a synchronized wrapper, synchronize on the wrapper for the duration of iteration. For applications with substantial concurrent access, evaluate concurrent collection designs according to their ordering and performance requirements.
24. Best Practices for Using Sets
- Declare variables using the Set interface when the implementation details are not important.
- Choose HashSet when order is unimportant and LinkedHashSet when insertion order matters.
- Use generics such as
Set<String> or Set<Integer> to improve type safety.
- Implement
equals() and hashCode() correctly for custom objects.
- Avoid changing fields used by equality and hashing while objects are stored in a set.
- Do not depend on the output order of HashSet.
- Validate input before adding elements when null or invalid values are not meaningful.
- Use a copy before applying destructive set operations if the original collection must remain unchanged.
- Remember that neither HashSet nor LinkedHashSet is automatically thread-safe.
25. Practical Project: Removing Duplicate Email Addresses
Scenario: A registration system receives a list of email addresses. The application must remove duplicate entries while preserving the order in which each address first appeared.
Program
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class UniqueEmailExample {
public static void main(String[] args) {
List<String> emailList = Arrays.asList(
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]"
);
Set<String> uniqueEmails =
new LinkedHashSet<>(emailList);
System.out.println("Original emails:");
System.out.println(emailList);
System.out.println("Unique emails:");
System.out.println(uniqueEmails);
System.out.println("Unique email count: " + uniqueEmails.size());
}
}
Expected Output
Original emails:
[[email protected], [email protected], [email protected], [email protected], [email protected]]
Unique emails:
[[email protected], [email protected], [email protected]]
Unique email count: 3
Project Explanation
- The program creates a List containing repeated email addresses.
- A LinkedHashSet is constructed from the list.
- Duplicate addresses are removed according to String equality.
- The first occurrence of each address determines its position in the resulting set.
- The program displays the unique addresses and their count.
Real-world consideration: Email addresses may require validation and a deliberate normalization policy. A Set compares Strings according to their equality rules; it does not automatically validate email addresses or treat different letter casing as equivalent.
26. Practice Exercises
- Create a HashSet of five programming languages and print its elements.
- Add duplicate integers to a HashSet and verify that duplicates are not stored.
- Create a LinkedHashSet of city names and confirm that insertion order is preserved.
- Write a program to check whether a particular employee ID exists in a set.
- Remove a specified value from a LinkedHashSet and display the remaining elements.
- Convert a List containing repeated names into a LinkedHashSet.
- Find the union, intersection, and difference of two integer sets.
- Create a Student class and use an appropriate equality definition to prevent duplicate student IDs.
- Convert a Set into a List and sort its values.
- Build a simple registration program that tracks unique course codes.
27. Interview Questions and Answers
Q1. What is the Set interface in Java?
Answer: Set is an interface in the Java Collections Framework that represents a collection of unique elements.
Q2. Does Set allow duplicate values?
Answer: No. A Set prevents duplicate elements according to its equality rules.
Q3. What is HashSet?
Answer: HashSet is a Set implementation that uses hashing to store unique elements and does not guarantee iteration order.
Q4. What is LinkedHashSet?
Answer: LinkedHashSet is a Set implementation that preserves insertion order while preventing duplicate elements.
Q5. What is the main difference between HashSet and LinkedHashSet?
Answer: HashSet does not guarantee iteration order, whereas LinkedHashSet preserves insertion order.
Q6. Can HashSet contain null?
Answer: Yes. HashSet permits one null element.
Q7. Can LinkedHashSet contain null?
Answer: Yes. LinkedHashSet also permits one null element.
Q8. Why are equals() and hashCode() important in a Set?
Answer: Hash-based sets use hash codes to help locate elements and equality checks to determine whether elements are duplicates. These methods must follow their contracts for correct behavior.
Q9. Can we access Set elements using an index?
Answer: No. The Set interface does not support index-based access. Use iteration or convert the set to a List when needed.
Q10. How do you remove duplicates from a List while preserving order?
Answer: Create a LinkedHashSet from the List, for example, Set<String> unique = new LinkedHashSet<>(list);.
Q11. Are HashSet and LinkedHashSet synchronized?
Answer: No. They are not synchronized by default. Suitable synchronization is required for concurrent access when modifications may occur.
Q12. How can you find common elements between two sets?
Answer: Copy one set and call retainAll() with the other set.
28. Quick Revision Checklist
- Set: An interface for collections of unique elements.
- HashSet: A hash-based implementation without guaranteed iteration order.
- LinkedHashSet: A hash-based implementation that preserves insertion order.
- Duplicates: Not stored as separate elements.
- Null: HashSet and LinkedHashSet permit one null element.
- Common methods:
add(), addAll(), contains(), remove(), size(), isEmpty(), and clear().
- Set operations: Use
addAll() for union, retainAll() for intersection, and removeAll() for difference.
- Custom objects: Implement
equals() and hashCode() consistently.
- Thread safety: Neither HashSet nor LinkedHashSet is synchronized by default.
29. Conclusion
The Set interface is useful whenever a Java application needs to maintain a collection of unique values. HashSet is a good general-purpose choice when iteration order does not matter. LinkedHashSet is more appropriate when duplicate prevention and insertion-order preservation are both required.
Understanding how sets handle duplicates, hashing, equality, iteration, and collection operations helps you write cleaner and more efficient Java programs. Practice these examples and exercises to strengthen your understanding of the Java Collections Framework.
30. Learning Resources