LinkedHashMap
About
LinkedHashMap is a subclass of HashMap introduced in Java 1.4. It maintains the order of elements, unlike HashMap, which is unordered. This class is part of the java.util package and implements the Map interface. The order of elements can be insertion order or access order, depending on the configuration.
Insertion Order: The order in which entries are inserted is preserved.
Access Order: The order is updated based on recent access if configured.
Features
Preserves Order: Maintains insertion or access order.
Backed by HashMap: Internally uses a
HashMapfor storage.Doubly Linked List: Maintains a linked list of entries for ordering.
Allows Null Keys and Values: Similar to
HashMap, it allows onenullkey and multiplenullvalues.Not Thread-Safe: By default, it is not synchronized. Use
Collections.synchronizedMap()for thread-safe operations.Performance: Slightly slower than
HashMapdue to linked list maintenance but faster thanTreeMapfor ordered operations.
Internal Working
The LinkedHashMap combines the functionality of HashMap with a doubly linked list to maintain insertion or access order.
Data Structure
LinkedHashMap relies on two main data structures:
HashMap:
Stores the key-value pairs in an array of buckets.
Each bucket contains a singly linked list (or a tree for large buckets in modern Java versions).
Hashing is used to distribute keys uniformly across buckets.
Doubly Linked List:
Maintains the order of entries.
Each entry in
LinkedHashMapis linked to the previous and next entries using two additional pointers (beforeandafter).
Components
Entry Class: Each entry in
LinkedHashMapis represented by an inner classLinkedHashMap.Entry<K, V>, which extendsHashMap.Node<K, V>. It has two extra fields:before: Points to the previous entry in the linked list.after: Points to the next entry in the linked list.
static class Entry<K, V> extends HashMap.Node<K, V> {
Entry<K, V> before, after;
Entry(int hash, K key, V value, Node<K, V> next) {
super(hash, key, value, next);
}
}Head and Tail Pointers:
The doubly linked list is anchored by a
headandtail.The
headpoints to the first inserted entry.The
tailpoints to the most recently inserted (or accessed, in access-order mode) entry.
How Order Is Maintained
Insertion Order:
When a new entry is added, it is appended to the end of the doubly linked list.
This preserves the order of insertion.
Access Order:
If
accessOrderis set totrue, the linked list is updated every time an entry is accessed (viaget,put, orputIfAbsentmethods).The accessed entry is moved to the end of the list.
Rehashing
Triggered when the size exceeds the load factor threshold.
The
HashMapresizes its bucket array, and entries are rehashed to new buckets.During rehashing:
The linked list structure is preserved.
The
beforeandafterpointers of all entries remain intact.
Operations
put(K key, V value)
Calculates the hash for the key.
Determines the bucket index based on the hash.
Checks if the key already exists:
If yes, updates the value.
If no, creates a new
Entry.
Inserts the entry in the
HashMap.Updates the doubly linked list:
Links the new entry to the current
tail.Updates the
tailpointer.
get(Object key)
Uses the hash to locate the bucket.
Searches for the key in the bucket’s chain.
If the key is found:
Returns the value.
Updates the order in the doubly linked list if
accessOrderistrue.
remove(Object key)
Locates the entry in the
HashMapusing the hash.Removes the entry from the bucket's chain.
Updates the doubly linked list:
Unlinks the entry from its
beforeandafterneighbors.Adjusts the
headortailpointers if necessary.
Key Methods
Method
Description
put(K key, V value)
Adds a key-value pair. If the key exists, updates the value.
get(Object key)
Retrieves the value associated with the key. Updates order if accessOrder is true.
remove(Object key)
Removes the key-value pair for the specified key.
containsKey(Object key)
Checks if the map contains the specified key.
containsValue(Object value)
Checks if the map contains the specified value.
keySet()
Returns a Set of keys in order.
values()
Returns a Collection of values in order.
entrySet()
Returns a Set of key-value mappings in order.
clear()
Removes all mappings.
removeEldestEntry(Map.Entry<K,V>)
Determines whether to remove the eldest entry when a new entry is added.
Big-O for Operations
Operation
Average Case
Worst Case
Put
O(1)
O(n)
Get
O(1)
O(n)
Remove
O(1)
O(n)
Iteration
O(n)
O(n)
Limitations
Memory Overhead: The doubly linked list adds memory overhead compared to
HashMap.Not Thread-Safe: Requires external synchronization for concurrent use.
Slightly Slower Than
HashMap: Maintaining order introduces additional overhead.Unpredictable Behavior with Null Keys: While it allows
nullkeys, unexpected behaviors may occur ifnullis frequently accessed in access-order mode.
Real-World Usage
Caches: Used to implement Least Recently Used (LRU) caches by overriding
removeEldestEntry().Maintaining Order: Useful in applications where insertion or access order is critical.
Data Stores: Ideal for implementing ordered data stores, like log files or access history.
Examples
1. Basic Operations
import java.util.LinkedHashMap;
public class LinkedHashMapExample {
public static void main(String[] args) {
LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
// Adding elements
map.put(1, "Alice");
map.put(2, "Bob");
map.put(3, "Charlie");
System.out.println(map); // Output: {1=Alice, 2=Bob, 3=Charlie}
// Accessing elements
System.out.println("Value for key 2: " + map.get(2)); // Output: Value for key 2: Bob
// Removing an element
map.remove(1);
System.out.println(map); // Output: {2=Bob, 3=Charlie}
// Iterating through entries
for (var entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
// Output:
// 2: Bob
// 3: Charlie
}
}
}2. Access Order
import java.util.LinkedHashMap;
public class AccessOrderExample {
public static void main(String[] args) {
LinkedHashMap<Integer, String> map = new LinkedHashMap<>(16, 0.75f, true);
map.put(1, "Alice");
map.put(2, "Bob");
map.put(3, "Charlie");
// Accessing elements
map.get(1);
map.get(3);
System.out.println(map); // Output: {2=Bob, 1=Alice, 3=Charlie}
}
}3. Implementing LRU Cache
import java.util.LinkedHashMap;
import java.util.Map;
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
}
public class LRUCacheExample {
public static void main(String[] args) {
LRUCache<Integer, String> cache = new LRUCache<>(3);
cache.put(1, "Alice");
cache.put(2, "Bob");
cache.put(3, "Charlie");
System.out.println(cache); // Output: {1=Alice, 2=Bob, 3=Charlie}
cache.get(1); // Access key 1
cache.put(4, "Diana"); // Add a new entry
System.out.println(cache); // Output: {2=Bob, 3=Charlie, 1=Alice}
}
}Last updated