Back to Blog
Java

Java LinkedHashSet Usage: Insertion Order and Tradeoffs

Learn how to use Java LinkedHashSet to preserve insertion order, understand its performance and memory tradeoffs, and compare it with HashSet and TreeSet.

LinkedHashSetJava CollectionsInsertion OrderHashSetPerformance
Diagram showing a Java LinkedHashSet preserving insertion order with a linked list connecting elements in a hash table.

LinkedHashSet is a HashSet subclass that adds linked-list bookkeeping to preserve insertion order. It supports the same duplicate-rejection behavior as HashSet, with average O(1) add, remove, and contains operations, but iteration follows insertion order.

What LinkedHashSet Guarantees

LinkedHashSet is a subclass of HashSet that adds a linked list to track insertion order. When you iterate over a LinkedHashSet, elements appear in the order they were inserted. This is the primary difference from HashSet, which makes no ordering guarantees. The linked list also means that re-inserting an element that already exists does not change its position; the set does not allow duplicates, so an existing element remains at its original insertion position.

Creating and Populating a LinkedHashSet

You can create a LinkedHashSet with its default constructor, or with an initial capacity and load factor, similar to HashSet. The default capacity is 16 and the load factor is 0.75. Here is a basic example:

import java.util.LinkedHashSet; import java.util.Set; Set<String> visited = new LinkedHashSet<>(); visited.add("home"); visited.add("work"); visited.add("gym"); System.out.println(visited); // [home, work, gym]

The iteration order matches the order of insertion. If you add an element that already exists, the set is unchanged, and the element stays in its original position.

Iteration Order and Removal Behavior

Removing an element from a LinkedHashSet does not affect the order of the remaining elements. The linked list is updated to skip the removed node, so subsequent iteration still follows the original insertion order for the remaining elements. This is useful when you need a set that also remembers the order in which items were added, for example, when implementing a first-in-first-out (FIFO) cache or tracking the sequence of visited pages.

Consider this example:

LinkedHashSet<Integer> numbers = new LinkedHashSet<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.remove(2); System.out.println(numbers); // [1, 3]

The order of 1 and 3 is preserved.

Performance and Memory Tradeoffs

LinkedHashSet inherits the O(1) average time complexity for add, remove, and contains operations from HashSet. However, the linked list adds overhead. Each element is stored as a node in a doubly linked list, which requires extra memory for the previous and next pointers. This means LinkedHashSet uses more memory than HashSet for the same number of elements. The exact overhead depends on the JVM and element type, but every entry carries additional references.

Iteration over a LinkedHashSet can be faster than over a HashSet in practice because the linked list provides a direct path through the elements, whereas HashSet iteration requires traversing the hash table's buckets and handling empty slots. For small sets the difference is usually negligible.

Comparing LinkedHashSet with HashSet and TreeSet

FeatureHashSetLinkedHashSetTreeSet
OrderingNoneInsertion orderSorted order (natural or comparator)
Time complexity (add/remove/contains)O(1) averageO(1) averageO(log n)
Memory overheadLowestModerate (linked list)Higher (tree nodes)
Use caseGeneral-purpose setWhen insertion order mattersWhen sorted iteration is required

Choose LinkedHashSet when you need a set with predictable iteration order and do not need sorting. If you need sorted order, TreeSet is the right choice. If order does not matter, HashSet is more memory-efficient.

Common Pitfalls and Edge Cases

One common mistake is assuming that LinkedHashSet is thread-safe. It is not. If multiple threads access the set concurrently, you must synchronize externally or use a thread-safe set implementation such as Collections.synchronizedSet(new LinkedHashSet<>()), or ConcurrentSkipListSet if you also need sorted order. You still need to synchronize while iterating over a collection returned by Collections.synchronizedSet.

Another edge case is overriding equals() and hashCode() inconsistently in the element class. The set relies on both methods to identify duplicates. If they are inconsistent, elements may not be treated as duplicates correctly, and the set can contain objects that should be considered equal. Keep equals() and hashCode() consistent.

LinkedHashSet permits at most one null element, exactly like HashSet. Adding null after it is already present leaves the set unchanged, and the null element appears in its insertion position during iteration.

When to Choose LinkedHashSet

Use LinkedHashSet when you need a set that:

  • Rejects duplicates.
  • Provides O(1) average performance for add, remove, and contains.
  • Requires iteration in insertion order.

Typical use cases include maintaining a list of unique items in the order they were encountered, such as tracking visited URLs, storing recently encountered items, or implementing a simple FIFO cache (with additional logic for eviction). If you need to remove the oldest element when the set reaches a capacity, you can combine LinkedHashSet with a custom eviction policy by iterating and removing the first element. If you need true LRU behavior based on access order, consider LinkedHashMap with access ordering enabled instead.

Mutable Elements and Hash Codes

One subtle behavior is that LinkedHashSet's iteration order is maintained by the linked list, not by hash codes, so rehashing does not change iteration order. However, if you modify fields that participate in equals() or hashCode() after insertion, the set's behavior becomes undefined because the element's hash bucket may no longer match its current hash code. To avoid this, make elements immutable or do not change their hash-relevant fields after insertion.

Java LinkedHashSet Usage: Ordering, Performance, and Code Examples | RYUSLOG DEV