LinkedHashSet
LinkedHashSet gives you the best of two worlds: the fast duplicate-rejection of a hash set and a predictable iteration order that matches the order you inserted elements. If you have ever been annoyed that HashSet shuffles your elements in a seemingly random order, LinkedHashSet is the fix.
What is LinkedHashSet?
LinkedHashSet<E> is part of java.util and extends HashSet. It implements the Set interface, so all the familiar rules still apply — no duplicate elements, no positional access. The key difference is that it maintains a doubly-linked list running through its entries in insertion order.
import java.util.LinkedHashSet;
public class LinkedHashSetBasics {
public static void main(String[] args) {
LinkedHashSet<String> languages = new LinkedHashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("Go");
languages.add("Java"); // duplicate — silently ignored
System.out.println(languages);
System.out.println("Size: " + languages.size());
}
}
Output:
[Java, Python, Go]
Size: 3
Notice that the output order is exactly the insertion order, and the duplicate "Java" was rejected without throwing any exception.
Creating a LinkedHashSet
You can construct a LinkedHashSet in several ways:
import java.util.LinkedHashSet;
import java.util.List;
public class CreatingLinkedHashSet {
public static void main(String[] args) {
// 1. Default capacity (16) and load factor (0.75)
LinkedHashSet<Integer> set1 = new LinkedHashSet<>();
// 2. With an initial capacity hint
LinkedHashSet<Integer> set2 = new LinkedHashSet<>(32);
// 3. With initial capacity and load factor
LinkedHashSet<Integer> set3 = new LinkedHashSet<>(32, 0.9f);
// 4. Copy from another collection (preserves iteration order)
List<String> list = List.of("red", "green", "blue", "red");
LinkedHashSet<String> set4 = new LinkedHashSet<>(list);
System.out.println(set4); // [red, green, blue]
}
}
Output:
[red, green, blue]
Tip: Initializing from a
Listis a popular one-liner for deduplicating a list while keeping its order — something neither a plainHashSetnor sorting can guarantee simultaneously.
Common Methods
LinkedHashSet inherits all its methods from HashSet and ultimately from Collection. Here are the ones you will use most often:
| Method | Description |
|---|---|
add(E e) | Adds element; returns false if already present |
remove(Object o) | Removes the element; returns false if absent |
contains(Object o) | Returns true if the element exists |
size() | Number of unique elements |
isEmpty() | true when the set has no elements |
clear() | Removes all elements |
iterator() | Returns an iterator in insertion order |
addAll(Collection c) | Adds all elements from another collection (union) |
retainAll(Collection c) | Keeps only elements that appear in c (intersection) |
removeAll(Collection c) | Removes all elements that appear in c (difference) |
import java.util.LinkedHashSet;
public class LinkedHashSetMethods {
public static void main(String[] args) {
LinkedHashSet<String> steps = new LinkedHashSet<>();
steps.add("Clone");
steps.add("Build");
steps.add("Test");
steps.add("Deploy");
System.out.println("Contains 'Test': " + steps.contains("Test"));
steps.remove("Build");
System.out.println("After remove: " + steps);
System.out.println("Size: " + steps.size());
}
}
Output:
Contains 'Test': true
After remove: [Clone, Test, Deploy]
Size: 3
Iterating Over a LinkedHashSet
Because the linked list preserves insertion order, iteration is fully predictable — unlike HashSet.
import java.util.Iterator;
import java.util.LinkedHashSet;
public class IteratingLinkedHashSet {
public static void main(String[] args) {
LinkedHashSet<String> days = new LinkedHashSet<>();
days.add("Monday");
days.add("Tuesday");
days.add("Wednesday");
// for-each (most common)
for (String day : days) {
System.out.println(day);
}
// Iterator (needed if you want to remove during traversal)
Iterator<String> it = days.iterator();
while (it.hasNext()) {
String d = it.next();
if (d.equals("Tuesday")) {
it.remove(); // safe removal
}
}
System.out.println("After removal: " + days);
}
}
Output:
Monday
Tuesday
Wednesday
After removal: [Monday, Wednesday]
Warning: Never call
set.remove()directly while iterating with a for-each loop — it will throwConcurrentModificationException. Always useiterator.remove()instead.
Practical Example: Deduplicating a List While Keeping Order
A classic real-world use case — imagine processing user-submitted tags where duplicates may appear but order matters for display:
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
public class DeduplicateList {
public static void main(String[] args) {
List<String> rawTags = Arrays.asList(
"java", "oop", "java", "collections", "oop", "generics"
);
// Deduplicate, preserving first-seen order
LinkedHashSet<String> uniqueTags = new LinkedHashSet<>(rawTags);
System.out.println(uniqueTags);
}
}
Output:
[java, oop, collections, generics]
LinkedHashSet with Custom Objects
When you store custom objects, you must override both equals() and hashCode(). Without them, two logically identical objects will be treated as distinct because Object’s default implementations compare by memory address.
import java.util.LinkedHashSet;
import java.util.Objects;
class Student {
String name;
int id;
Student(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Student s)) return false;
return id == s.id && Objects.equals(name, s.name);
}
@Override
public int hashCode() {
return Objects.hash(name, id);
}
@Override
public String toString() {
return name + "(" + id + ")";
}
}
public class LinkedHashSetCustom {
public static void main(String[] args) {
LinkedHashSet<Student> roster = new LinkedHashSet<>();
roster.add(new Student("Alice", 1));
roster.add(new Student("Bob", 2));
roster.add(new Student("Alice", 1)); // duplicate — rejected
System.out.println(roster);
}
}
Output:
[Alice(1), Bob(2)]
Note: See equals() and hashCode() for the full contract and why both methods must always be overridden together.
LinkedHashSet vs HashSet vs TreeSet
Picking the right Set implementation is straightforward once you know what each one prioritises:
| Feature | HashSet | LinkedHashSet | TreeSet |
|---|---|---|---|
| Iteration order | Random (unpredictable) | Insertion order | Sorted (natural / Comparator) |
Allows null? | One null | One null | No null |
Performance (add/contains/remove) | O(1) avg | O(1) avg (tiny overhead) | O(log n) |
| Memory overhead | Lowest | Small (extra linked-list pointers) | Higher (tree nodes) |
| Extra navigational methods? | No | No | Yes (first(), headSet(), …) |
Use LinkedHashSet when:
- You need uniqueness and a predictable iteration order.
- You are deduplicating a list and must preserve the original encounter order.
- You are building a cache or LRU-like structure where insertion order carries meaning.
Use HashSet when order does not matter and you want the absolute minimum memory footprint.
Use TreeSet when you need elements sorted rather than ordered by insertion.
Under the Hood
Internally, LinkedHashSet does almost nothing itself — its entire body is a single constructor that forwards to LinkedHashMap. All the real work lives in LinkedHashMap, which extends HashMap and adds one extra pair of pointers per entry (before and after) that form a doubly-linked list across all map entries in insertion order.
When you call add("Java"):
- The element’s
hashCode()is computed and used to find the correct bucket in the backingLinkedHashMap. - If a key equal to
"Java"already exists, the add is a no-op andfalseis returned. - Otherwise a new
LinkedHashMap.Entryis created, placed in the bucket, and appended to the tail of the linked list.
When you iterate, the iterator walks the linked list — not the hash table buckets — so you always see elements in insertion order regardless of hash distribution.
Memory cost: each entry carries two extra object references compared to a plain HashMap entry. For millions of entries this can add up; if order is truly irrelevant, prefer HashSet.
Thread safety: LinkedHashSet is not thread-safe. If multiple threads access it concurrently and at least one modifies it, synchronize externally or wrap it:
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
Set<String> safeSet = Collections.synchronizedSet(new LinkedHashSet<>());
For high-concurrency scenarios, consider the concurrent collections like ConcurrentHashMap.newKeySet() — though that one does not preserve insertion order.
Related Topics
- HashSet — the fastest
Setwhen you don’t need a guaranteed iteration order - TreeSet —
Setthat keeps elements in sorted order with rich navigational methods - Set Interface — the contract all
Setimplementations share - HashMap — the map sibling that
HashSet(andLinkedHashSet) is built on - LinkedHashMap — the underlying map that powers
LinkedHashSet’s ordering - Comparable — how
TreeSetsorts custom objects naturally