Back to Blog
Java

Java LinkedHashSet: Order and Performance

java linkedhashset: Learn how Java LinkedHashSet preserves insertion order while offering HashSet's constant-time operations, with practical examples and tradeoffs.

LinkedHashSetJava CollectionsHashSetInsertion OrderJava Data StructuresIteration Order
Diagram showing a Java LinkedHashSet with a hash table and a doubly linked list preserving insertion order.

java linkedhashset requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you need a Set that preserves the order in which elements were added, java.util.LinkedHashSet is the standard choice. It combines the constant-time operations of a hash set with a predictable iteration order. This makes it useful for caching, deduplication with order retention, and any scenario where the sequence of first occurrence matters.

How LinkedHashSet Maintains Insertion Order

LinkedHashSet extends HashSet and uses a hash table internally, but it also maintains a doubly linked list that connects all entries in the order they were inserted. This linked list is what gives the set its predictable iteration order. Unlike LinkedHashMap, which can be configured for access order, LinkedHashSet always uses insertion order. Re-adding an element that already exists does not change its position in the iteration order; the set simply ignores the duplicate.

The internal structure means that iteration order is deterministic and matches the order of first insertion. This is not a sorting order; it is purely the sequence in which elements were added. If you remove an element and then add it again, it appears at the end, because the removal breaks the link and the re-insertion creates a new link.

Creating and Using a LinkedHashSet

Using LinkedHashSet is straightforward. The class provides the same constructors as HashSet, including a default constructor, one with initial capacity, one with load factor, and one that accepts another collection.

import java.util.LinkedHashSet; import java.util.Set; Set<String> visitedUrls = new LinkedHashSet<>(); visitedUrls.add("/home"); visitedUrls.add("/products"); visitedUrls.add("/cart"); visitedUrls.add("/home"); // duplicate, ignored System.out.println(visitedUrls); // Output: [/home, /products, /cart]

The iteration order reflects the first occurrence of each element. This is useful when you need to process unique items in the order they were first seen, such as building a list of unique page views or preserving the order of user actions.

You can iterate using an enhanced for loop or an iterator:

for (String url : visitedUrls) { System.out.println("Visited: " + url); }

The iterator is fail-fast: if the set is structurally modified after the iterator is created (except through the iterator's own remove method), a ConcurrentModificationException is thrown. This is consistent with other collection iterators in the Java Collections Framework.

LinkedHashSet vs HashSet vs TreeSet

The choice among these three Set implementations depends on whether you need ordering and what kind of ordering. The table below summarizes the key differences.

FeatureHashSetLinkedHashSetTreeSet
Iteration orderUnspecifiedInsertion orderSorted (natural or comparator)
Internal structureHash tableHash table + doubly linked listRed-black tree
Time complexityO(1) averageO(1) averageO(log n)
Memory overheadLowestModerate (linked list)Higher (tree nodes)
Null handlingAllows nullAllows nullDoes not allow null (natural ordering)

Use HashSet when order does not matter and you want the lowest memory footprint. Use TreeSet when you need elements sorted according to a comparator or natural order, and you are willing to accept O(log n) operations. Use LinkedHashSet when you need to preserve insertion order without sacrificing the constant-time performance of a hash-based set.

Performance and Memory Considerations

LinkedHashSet provides O(1) average time for add, remove, and contains, just like HashSet. The extra linked list adds a constant overhead per element: each entry must store references to the previous and next nodes. This increases memory consumption compared to HashSet, but the impact is usually modest unless you are storing millions of elements.

The main performance cost is not in the operations themselves but in the iteration. Because the linked list maintains order, iterating over a LinkedHashSet is generally faster than iterating over a HashSet when order is required, since it avoids the need to sort or to traverse a sparse hash table. However, the difference is often negligible for small collections.

When initializing a LinkedHashSet with a known large number of elements, setting an appropriate initial capacity can reduce rehashing overhead. The same guidance applies as for HashSet: choose an initial capacity that is roughly the expected number of elements divided by the load factor (default 0.75) to avoid repeated resizing.

Common Pitfalls and Edge Cases

One common mistake is assuming that LinkedHashSet sorts elements. It does not. If you need sorted iteration, use TreeSet or explicitly sort the elements after extraction.

Another pitfall is using mutable objects as keys. If an element's hashCode() changes after it is inserted, the set will not be able to locate it correctly, and behavior becomes unpredictable. This applies to all hash-based sets, not just LinkedHashSet. Ensure that elements are effectively immutable or that their hash code does not change while they are stored.

The equals and hashCode contract is critical. Two elements that are equal must have the same hash code. If you override equals without hashCode, the set may contain duplicates or fail to find elements. Use Objects.equals and Objects.hash for reliable implementations.

Null handling: LinkedHashSet permits one null element, just like HashSet. If you attempt to add a second null, it is ignored because the set already contains null. This is fine for most use cases, but be aware that some operations like TreeSet do not allow null at all.

When to Choose LinkedHashSet

Choose LinkedHashSet when you need to deduplicate elements while preserving the order of their first appearance. Typical use cases include:

  • Maintaining a list of unique user actions in the order they occurred.
  • Caching keys in insertion order for LRU-like eviction (though LinkedHashMap is more direct for that).
  • Producing a unique list of configuration names or options in the order they were defined.

For example, suppose you are processing a stream of events and want to collect the distinct event types in the order they first appear:

List<String> events = List.of("LOGIN", "SEARCH", "LOGIN", "PURCHASE", "SEARCH"); Set<String> uniqueEvents = new LinkedHashSet<>(events); System.out.println(uniqueEvents); // [LOGIN, SEARCH, PURCHASE]

The constructor that accepts a Collection copies the elements and preserves the order of the source collection, which is convenient for deduplication.

If you need to remove the oldest element when the set reaches a certain size, a LinkedHashSet alone does not provide that capability. You would need to combine it with a Deque or use LinkedHashMap with access order. The set's insertion order is static; it does not change when elements are accessed.

In concurrent environments, LinkedHashSet is not thread-safe. Use Collections.synchronizedSet(new LinkedHashSet<>()) if you need synchronized access, or use a ConcurrentSkipListSet if you need a thread-safe sorted set. The synchronized wrapper maintains the insertion order but requires external synchronization during iteration to avoid ConcurrentModificationException.

java linkedhashset: Practical Usage and Code Examples | RYUSLOG DEV