Topic 92 of 119 10. Java Collections Framework Interactive Tutorial

Java HashSet and LinkedHashSet: Unique Elements and Ordering

9 mins read Last updated: 22 Aug 2026 30,916 views

Direct Answer & Core Concept

HashSet and LinkedHashSet are Java collection classes used when you need a set of unique elements. Unlike a List, a set does not allow duplicate elements.

Both classes are part of the Java Collections Framework and implement the Set interface. HashSet focuses on efficient hash-based storage without guaranteeing iteration order, while LinkedHashSet additionally preserves insertion order.

Understanding the difference between these two collections is important when choosing the right data structure for storing unique values.

Detailed Explanation & Mechanics

What Is HashSet in Java?

HashSet is a collection class from the java.util package that stores unique elements using hashing.

Example:

Main.java
Run Online
import java.util.HashSet;
HashSet<String> languages = new HashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("Java");
System.out.println(languages);

The second "Java" is not added because sets do not store duplicates.

The exact printed order should not be relied upon with HashSet.


What Is LinkedHashSet in Java?

LinkedHashSet is a subclass of HashSet that maintains a linked structure alongside its hash table so that iteration normally follows insertion order.

Example:

Main.java
Run Online
import java.util.LinkedHashSet;
LinkedHashSet<String> languages = new LinkedHashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
System.out.println(languages);

Output:

If you add a duplicate:

the existing element remains in its original position.


HashSet vs LinkedHashSet

The main difference is ordering.

FeatureHashSetLinkedHashSet
Allows duplicatesNoNo
Hash-basedYesYes
Preserves insertion orderNo guaranteeYes
nullAllows oneAllows one
Average basic operationsTypically O(1)Typically O(1)
Extra ordering structureNoYes
Memory overheadGenerally lowerGenerally higher

The complexity values are typical rather than absolute guarantees for every implementation or workload.


Importing HashSet

Use:

Main.java
Run Online
import java.util.HashSet;

Then create a set:

Main.java
Run Online
HashSet<String> names = new HashSet<>();

A more flexible declaration is:

Main.java
Run Online
Set<String> names = new HashSet<>();

This programs to the Set interface.


Importing LinkedHashSet

Use:

Main.java
Run Online
import java.util.LinkedHashSet;

Example:

Main.java
Run Online
LinkedHashSet<String> names = new LinkedHashSet<>();

Or:

Main.java
Run Online
Set<String> names = new LinkedHashSet<>();

The second form makes it easier to change the implementation later.


Creating a HashSet

The basic syntax is:

Main.java
Run Online
HashSet<Type> set = new HashSet<>();

For strings:

Main.java
Run Online
HashSet<String> names = new HashSet<>();

For integers:

Main.java
Run Online
HashSet<Integer> numbers = new HashSet<>();

Java collections store objects, so wrapper types such as Integer are used instead of primitive int.


Creating a LinkedHashSet

The syntax is similar:

Main.java
Run Online
LinkedHashSet<Type> set = new LinkedHashSet<>();

Example:

Main.java
Run Online
LinkedHashSet<String> cities = new LinkedHashSet<>();
cities.add("Kolkata");
cities.add("Delhi");
cities.add("Mumbai");
System.out.println(cities);

Output:

The insertion sequence is preserved during iteration.


Adding Elements to HashSet

Use the add() method.

Main.java
Run Online
HashSet<String> fruits = new HashSet<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Mango");

You can print the set:

Main.java
Run Online
System.out.println(fruits);

Unlike ArrayList, you should not expect a predictable iteration order.


Duplicate Elements in HashSet

One of the most important properties of a set is that duplicate elements are not stored.

Main.java
Run Online
HashSet<String> names = new HashSet<>();
names.add("Amit");
names.add("Rahul");
names.add("Amit");
System.out.println(names);

The set contains only:

The order shown by a HashSet is not guaranteed.


Checking Whether an Element Was Added

The add() method returns a boolean.

It returns true when the set changes because the element was added.

Main.java
Run Online
HashSet<String> names = new HashSet<>();
System.out.println(names.add("Amit"));

Output:

Adding the same element again:

Main.java
Run Online
System.out.println(names.add("Amit"));

Output:

This makes add() useful when you need to know whether a value was already present.


How HashSet Detects Duplicates

Hash-based sets use an object's hashCode() and equals() behavior to determine whether elements should be treated as duplicates.

Conceptually:

Main.java
Run Online
New object↓hashCode()↓Find possible location↓equals()↓Duplicate or new element?

A matching hash code alone is not enough to establish that two objects are equal.

The equals() method is also important.


hashCode() and equals()

For objects used in a HashSet, the equals() and hashCode() contract matters.

If two objects are considered equal according to equals(), they must return the same hash code.

For example:

Main.java
Run Online
class User {
    private int id;
    User(int id) {
        this.id = id;
    } @Override public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } if (!(obj instanceof User)) {
            return false;
        } User other =(User) obj;
        return id == other.id;
    } @Override public int hashCode() {
        return Integer.hashCode(id);
    }
}

Now two User objects with the same ID can be treated as equal by hash-based collections.


Why equals() and hashCode() Must Agree

Suppose:

Main.java
Run Online
User user1 = new User(101);
User user2 = new User(101);

If:

returns true, both objects must have the same hash code.

Otherwise, a HashSet can behave incorrectly because hash-based lookup relies on the contract between these methods.

This is one of the most important rules when storing custom objects in HashSet.


HashSet and null

A HashSet can contain one null element.

Example:

Main.java
Run Online
HashSet<String> values = new HashSet<>();
values.add("Java");
values.add(null);
values.add("Python");
values.add(null);
System.out.println(values);

Only one null can exist because a set does not allow duplicates.


LinkedHashSet and null

LinkedHashSet can also contain one null element.

Main.java
Run Online
LinkedHashSet<String> values = new LinkedHashSet<>();
values.add("Java");
values.add(null);
values.add("Python");
values.add(null);
System.out.println(values);

The duplicate null is ignored.


Checking Whether an Element Exists

Use:

Example:

Main.java
Run Online
HashSet<String> languages = new HashSet<>();
languages.add("Java");
languages.add("Python");
System.out.println(languages.contains("Java"));

Output:

If the element does not exist:

Main.java
Run Online
System.out.println(languages.contains("PHP"));

Output:

Hash-based lookup is typically O(1) on average.


Removing an Element

Use:

Example:

Main.java
Run Online
HashSet<String> languages = new HashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
languages.remove("Python");
System.out.println(languages);

The "Python" element is removed.

The remove() method returns true if the set contained the specified element and it was removed.


Checking the Size

Use:

Example:

Main.java
Run Online
HashSet<String> names = new HashSet<>();
names.add("Amit");
names.add("Rahul");
names.add("Priya");
System.out.println(names.size());

Output:

Duplicates do not increase the size.


Checking Whether a Set Is Empty

Use:

Example:

Main.java
Run Online
HashSet<String> names = new HashSet<>();
System.out.println(names.isEmpty());

Output:

After adding an element:

Main.java
Run Online
names.add("Amit");
System.out.println(names.isEmpty());

Output:


Clearing a HashSet

The clear() method removes every element.

Main.java
Run Online
HashSet<String> names = new HashSet<>();
names.add("Amit");
names.add("Rahul");
names.clear();
System.out.println(names);

Output:

The set becomes empty.


Iterating Through HashSet

You can use an enhanced for loop:

Main.java
Run Online
HashSet<String> languages = new HashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
for (String language : languages) {
    System.out.println(language);
}

Remember that the iteration order of a HashSet is not guaranteed.

Therefore, do not write application logic that depends on the order in which elements happen to be returned.


Iterating Through LinkedHashSet

The same loop can be used:

Main.java
Run Online
LinkedHashSet<String> languages = new LinkedHashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
for (String language : languages) {
    System.out.println(language);
}

Iteration follows insertion order.

Output:


HashSet and LinkedHashSet with Integer Values

Sets work with numeric wrapper classes too.

Main.java
Run Online
HashSet<Integer> numbers = new HashSet<>();
numbers.add(10);
numbers.add(20);
numbers.add(10);
numbers.add(30);
System.out.println(numbers);

The duplicate 10 is ignored.

With LinkedHashSet:

Main.java
Run Online
LinkedHashSet<Integer> numbers = new LinkedHashSet<>();
numbers.add(30);
numbers.add(10);
numbers.add(20);
numbers.add(10);
System.out.println(numbers);

The iteration order is:

The duplicate 10 does not create another entry.


Removing Duplicates from a List

One common use of HashSet is removing duplicates from a list.

Suppose you have:

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Amit");
names.add("Rahul");
names.add("Amit");
names.add("Priya");

You can create a set:

Main.java
Run Online
Set<String> uniqueNames = new HashSet<>(names);

Now only unique values remain.

However, because HashSet does not guarantee order, the original list order should not be relied upon.

If you want to remove duplicates while preserving the original insertion order, use LinkedHashSet:

Main.java
Run Online
Set<String> uniqueNames = new LinkedHashSet<>(names);

This is a very common practical use case.


Example: Remove Duplicates While Preserving Order

Main.java
Run Online
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Amit");
        names.add("Rahul");
        names.add("Amit");
        names.add("Priya");
        names.add("Rahul");
        Set<String> uniqueNames = new LinkedHashSet<>(names);
        System.out.println(uniqueNames);
    }
}

Output:

The duplicates are removed while the first-seen order is preserved.


HashSet vs LinkedHashSet Ordering

Consider the same values:

Main.java
Run Online
"Java""Python""C++""JavaScript"

With HashSet:

Main.java
Run Online
Set<String> set = new HashSet<>();

you must not depend on iteration order.

With LinkedHashSet:

Main.java
Run Online
Set<String> set = new LinkedHashSet<>();

iteration follows the insertion sequence.

This difference is the primary reason to choose LinkedHashSet over HashSet.


LinkedHashSet and Reinserting an Element

Suppose:

Main.java
Run Online
LinkedHashSet<String> languages = new LinkedHashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");

Now add "Java" again:

The set still contains:

The existing element is not moved to the end.

LinkedHashSet preserves insertion order of the elements that are present; adding an already-present element does not create a new insertion.


HashSet Performance

Typical average-case complexity for basic hash-based operations is:

OperationAverage Complexity
add()O(1)
remove()O(1)
contains()O(1)
size()O(1)

Worst-case behavior can differ depending on hashing, collisions, implementation details, and workload.

Do not treat O(1) as a guarantee that every operation always takes exactly the same amount of time.


LinkedHashSet Performance

LinkedHashSet provides similar typical average-case performance for basic set operations:

OperationAverage Complexity
add()O(1)
remove()O(1)
contains()O(1)
size()O(1)
IterationO(n)

It requires additional memory to maintain insertion-order information.

Therefore, if ordering is unnecessary, HashSet can be a simpler choice.


HashSet vs ArrayList

HashSet and ArrayList solve different problems.

FeatureArrayListHashSet
DuplicatesAllowedNot allowed
Insertion orderPreservedNo guarantee
Index-based accessYesNo
contains()Typically O(n)Typically O(1) average
Allows nullYesYes
Main purposeOrdered sequenceUnique elements

Use ArrayList when order and index access matter.

Use HashSet when uniqueness and fast membership testing matter.


LinkedHashSet vs ArrayList

LinkedHashSet is useful when you need both:

  1. Unique elements

  2. Predictable insertion-order iteration

For example:

Main.java
Run Online
LinkedHashSet<String> visitedPages = new LinkedHashSet<>();

This can represent unique pages while preserving the order in which they were first encountered.


HashSet vs TreeSet

Another important comparison is TreeSet.

FeatureHashSetLinkedHashSetTreeSet
Unique valuesYesYesYes
Hash-basedYesYesNo
Insertion orderNo guaranteeYesNo
Sorted orderNoNoYes
Typical basic operationO(1) averageO(1) averageO(log n)
Main purposeFast uniquenessUniqueness + insertion orderSorted uniqueness

If you need sorted elements, TreeSet is usually the relevant collection.


Set Interface

Both classes implement the Set interface.

This means you can write:

Main.java
Run Online
Set<String> names = new HashSet<>();

or:

Main.java
Run Online
Set<String> names = new LinkedHashSet<>();

The variable is declared using the interface while the object specifies the implementation.

This is an example of programming to an interface.


Common Set Operations

Java provides useful set operations through the Set interface.

Suppose:

Main.java
Run Online
Set<String> first = new HashSet<>();
first.add("Java");
first.add("Python");
first.add("C++");
Set<String> second = new HashSet<>();
second.add("Python");
second.add("JavaScript");

Union

Add all elements from the second set:

Main.java
Run Online
Set<String> union = new HashSet<>(first);
union.addAll(second);

Result:

The exact display order for HashSet is unspecified.


Intersection

Keep only elements found in both sets:

Main.java
Run Online
Set<String> intersection = new HashSet<>(first);
intersection.retainAll(second);

Result:


Difference

Remove elements found in another set:

Main.java
Run Online
Set<String> difference = new HashSet<>(first);
difference.removeAll(second);

Result:

These operations are useful when comparing groups of unique values.


Checking Whether One Set Contains Another

You can use:

Example:

Main.java
Run Online
Set<String> required = new HashSet<>();
required.add("Java");
required.add("Python");
Set<String> available = new HashSet<>();
available.add("Java");
available.add("Python");
available.add("C++");
System.out.println(available.containsAll(required));

Output:


HashSet with Custom Objects

Consider a Student class:

Main.java
Run Online
class Student {
    private int id;
    private String name;
    Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

You can store students:

Main.java
Run Online
HashSet<Student> students = new HashSet<>();
students.add(new Student(101, "Amit"));
students.add(new Student(102, "Rahul"));

If you want the set to recognize students according to their IDs, you need to implement equals() and hashCode() based on the desired identity.


A Practical Custom Object Example

Main.java
Run Online
import java.util.HashSet;
import java.util.Set;
import java.util.Objects;
class Product {
    private int id;
    private String name;
    Product(int id, String name) {
        this.id = id;
        this.name = name;
    } @Override public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } if (!(obj instanceof Product)) {
            return false;
        } Product other =(Product) obj;
        return id == other.id;
    } @Override public int hashCode() {
        return Objects.hash(id);
    }
}public class Main {
    public static void main(String[] args) {
        Set<Product> products = new HashSet<>();
        products.add(new Product(101, "Laptop"));
        products.add(new Product(101, "Laptop"));
        System.out.println(products.size());
    }
}

Output:

Both objects have the same logical ID, so the set treats them as equal.


Mutable Objects and HashSet

Be careful when using mutable objects as keys or set elements.

Suppose an object's fields used by equals() and hashCode() are changed after the object has been inserted into a HashSet.

The object may no longer be found correctly.

For example, conceptually:

Main.java
Run Online
Insert object↓Hash calculated↓Object stored↓Identity field changes↓New hash calculated↓contains() may fail

For this reason, fields involved in equality and hashing should generally not be changed while an object is being used as a hash-based collection element.


HashSet and Thread Safety

HashSet is not synchronized by default.

If multiple threads access and modify a shared set concurrently, use an appropriate concurrency strategy.

For example, depending on requirements, a concurrent set can be created using:

Main.java
Run Online
Set<String> names = ConcurrentHashMap.newKeySet();

Do not assume that wrapping a collection automatically makes every multi-step operation atomic.


LinkedHashSet and Thread Safety

LinkedHashSet is also not synchronized by default.

If concurrent access is required, choose a collection or synchronization strategy specifically designed for the application's concurrency requirements.


Initial Capacity and Load Factor

Hash-based collections use concepts such as:

  • Initial capacity

  • Load factor

  • Hash distribution

Example:

Main.java
Run Online
HashSet<String> names = new HashSet<>(100, 0.75f);

This creates a set with an initial capacity configuration and load factor.

Most applications can use the default settings.

Custom values are mainly useful when you understand the expected data size and performance requirements.


Common HashSet Mistakes

Mistake 1: Expecting HashSet to Preserve Order

Do not write code that depends on:

Main.java
Run Online
HashSet<String> names = new HashSet<>();

returning elements in insertion order.

If insertion order matters, consider:


Mistake 2: Using a Set When Duplicates Matter

If duplicate values are meaningful, use a List instead.

For example, a list of transaction records may legitimately contain repeated values.


Mistake 3: Forgetting equals() and hashCode()

When using custom objects, correct equals() and hashCode() implementations are essential for hash-based collections.


Mistake 4: Modifying Equality Fields After Insertion

Changing fields used by equals() and hashCode() can make an object difficult to find in a hash-based collection.


Mistake 5: Assuming O(1) Means Guaranteed Constant Time

Hash-based collections provide expected average-case constant-time performance for many basic operations, but actual performance depends on hashing, collisions, implementation, and workload.


Complete HashSet Example

Main.java
Run Online
import java.util.HashSet;
import java.util.Set;
public class Main {
    public static void main(String[] args) {
        Set<String> languages = new HashSet<>();
        languages.add("Java");
        languages.add("Python");
        languages.add("C++");
        languages.add("Java");
        System.out.println("Languages: " + languages);
        System.out.println( "Contains Java: " + languages.contains("Java"));
        languages.remove("Python");
        System.out.println("After removal: " + languages);
        System.out.println("Size: " + languages.size());
    }
}

The duplicate "Java" is ignored.

The exact printed order is not guaranteed.


Complete LinkedHashSet Example

Main.java
Run Online
import java.util.LinkedHashSet;
import java.util.Set;
public class Main {
    public static void main(String[] args) {
        Set<String> languages = new LinkedHashSet<>();
        languages.add("Java");
        languages.add("Python");
        languages.add("C++");
        languages.add("Java");
        for (String language : languages) {
            System.out.println(language);
        }
    }
}

Output:

The duplicate is removed while insertion order is preserved.


Real-World Use Cases

1. Removing Duplicate User IDs

Main.java
Run Online
Set<Integer> userIds = new HashSet<>();

Useful when you only care whether an ID has already been seen.


2. Preserving Unique Search Terms

Main.java
Run Online
Set<String> searchTerms = new LinkedHashSet<>();

This keeps only unique terms while preserving the order in which they were first encountered.


3. Tracking Visited URLs

Main.java
Run Online
Set<String> visitedUrls = new HashSet<>();

A crawler can use a set to quickly check whether a URL has already been processed.


4. Unique Tags

Main.java
Run Online
Set<String> tags = new LinkedHashSet<>();

This can preserve the order of the first appearance of tags while preventing duplicates.


5. Permission Names

Main.java
Run Online
Set<String> permissions = new HashSet<>();

A set naturally represents a collection where each permission should appear at most once.


When Should You Use HashSet?

Use HashSet when:

  • Duplicate values should be rejected.

  • You need fast average-case membership checks.

  • Element ordering is not important.

  • You frequently perform add, remove, and contains operations.

  • You need to represent a mathematical-style set of unique values.

Example:

Main.java
Run Online
Set<String> uniqueEmails = new HashSet<>();

When Should You Use LinkedHashSet?

Use LinkedHashSet when:

  • Duplicate values should be rejected.

  • Insertion order matters during iteration.

  • You need predictable iteration order.

  • You are removing duplicates while preserving first-seen order.

Example:

Main.java
Run Online
Set<String> uniqueNames = new LinkedHashSet<>();

HashSet, LinkedHashSet, and TreeSet Decision Guide

A simple decision rule is:

Main.java
Run Online
Need unique values? | Yes | +---- Need sorted order? | | | Yes → TreeSet | +---- Need insertion order? | | | Yes → LinkedHashSet | +---- No order requirement | HashSet

This helps you choose a set implementation based on the behavior your application actually needs.


Quick Revision

ConceptHashSetLinkedHashSet
Unique elementsYesYes
Duplicate valuesRejectedRejected
Insertion orderNot guaranteedPreserved
Hash-basedYesYes
nullOne allowedOne allowed
contains()O(1) averageO(1) average
add()O(1) averageO(1) average
remove()O(1) averageO(1) average
Memory usageGenerally lowerGenerally higher
Best forUnique valuesUnique values + insertion order

Frequently Asked Questions

What is HashSet in Java?

HashSet is a Set implementation that stores unique elements using hash-based storage.

Does HashSet allow duplicates?

No. If an equal element is already present, adding it does not change the set.

Does HashSet maintain insertion order?

No. HashSet does not guarantee a particular iteration order.

What is LinkedHashSet?

LinkedHashSet is a set implementation that prevents duplicates while maintaining insertion order during iteration.

What is the difference between HashSet and LinkedHashSet?

Both store unique values using hashing. LinkedHashSet additionally maintains insertion order, which requires extra internal bookkeeping.

Can HashSet contain null?

Yes. A HashSet can contain one null element.

Can LinkedHashSet contain null?

Yes. A LinkedHashSet can contain one null element.

Why are equals() and hashCode() important?

Hash-based collections use these methods to determine object equality and locate elements. Equal objects must have the same hash code.

Which is faster, HashSet or LinkedHashSet?

Both generally provide similar average-case complexity for basic operations. LinkedHashSet maintains additional ordering information, so HashSet may use less memory when ordering is unnecessary.

How can I remove duplicates while preserving order?

Use a LinkedHashSet:

Main.java
Run Online
Set<String> unique = new LinkedHashSet<>(list);

Should I use HashSet or ArrayList?

Use HashSet when uniqueness and membership testing are important. Use ArrayList when ordering, duplicates, and indexed access are important.

Graphics and Design Ideas

1. HashSet Duplicate Removal

Show:

Main.java
Run Online
Input[Java][Python][Java][C++][Python]↓HashSet↓[Java][Python][C++]

The graphic should emphasize that duplicates are automatically rejected.


2. HashSet vs LinkedHashSet

Create a side-by-side visual:

Main.java
Run Online
HashSetJavaC++PythonJavaScriptOrder: Not GuaranteedLinkedHashSetJava → Python → C++ → JavaScriptOrder: Insertion Order

Use different icons for uniqueness and ordering.


3. equals() + hashCode() Diagram

Show the duplicate-detection process:

Main.java
Run Online
New Object↓hashCode()↓Hash Location↓equals()↓Already Equal? / \ Yes No↓↓Ignore Add

This is especially useful for explaining custom objects.


4. Set Selection Decision Tree

Create a beginner-friendly visual:

Main.java
Run Online
Need unique values? | YES | Need a specific order? / \ No Yes↓↓HashSet What order? / \ Insertion Sorted↓↓LinkedHashSet TreeSet

5. Real-World Use Case Cards

Create four visual cards:

Duplicate Removal
Remove repeated values from imported data.

Visited URLs
Track URLs that have already been processed.

Unique Tags
Store unique tags without duplicates.

Search Terms
Keep unique terms while preserving first-seen order with LinkedHashSet.

Runnable Code Example: Java HashSet and LinkedHashSet

Java 17+
Main.java
Run Online
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class SetDemo {
    public static void main(String[] args) {
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Zebra");
        hashSet.add("Apple");
        hashSet.add("Mango");
        hashSet.add("Apple");
        // Duplicate ignored
        Set<String> linkedSet = new LinkedHashSet<>();
        linkedSet.add("Zebra");
        linkedSet.add("Apple");
        linkedSet.add("Mango");
        System.out.println("HashSet (Unordered, Unique): " + hashSet);
        System.out.println("LinkedHashSet (Insertion Ordered): " + linkedSet);
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1

How does HashSet detect duplicate elements internally?

It puts the element as a key into an internal HashMap; if key.hashCode() and key.equals() match an existing key, the value is not added.
Q2

What happens if you add an object to a HashSet without overriding hashCode() and equals()?

Duplicate logical objects will be treated as distinct because default Object identity uses memory address comparison.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Always override both hashCode() and equals() together whenever creating custom classes intended for Sets and Maps.