LinkedHashMap Insertion Order in Java – Complete Guide

Overview

LinkedHashMap maintains a doubly-linked list running through all its entries, which defines the iteration ordering. By default, it maintains insertion order, but can also be configured to maintain access order.

Basic Insertion Order Example

import java.util.*;
public class LinkedHashMapInsertionOrder {
public static void main(String[] args) {
// Creating LinkedHashMap with default insertion order
LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
// Adding elements - order will be preserved
linkedHashMap.put("Apple", 1);
linkedHashMap.put("Banana", 2);
linkedHashMap.put("Orange", 3);
linkedHashMap.put("Mango", 4);
linkedHashMap.put("Grapes", 5);
System.out.println("LinkedHashMap (Insertion Order):");
linkedHashMap.forEach((key, value) -> System.out.println(key + " = " + value));
// Compare with HashMap (no order guarantee)
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("Apple", 1);
hashMap.put("Banana", 2);
hashMap.put("Orange", 3);
hashMap.put("Mango", 4);
hashMap.put("Grapes", 5);
System.out.println("\nHashMap (No Order Guarantee):");
hashMap.forEach((key, value) -> System.out.println(key + " = " + value));
}
}

Output:

LinkedHashMap (Insertion Order):
Apple = 1
Banana = 2
Orange = 3
Mango = 4
Grapes = 5
HashMap (No Order Guarantee):
Orange = 3
Apple = 1
Grapes = 5
Banana = 2
Mango = 4

LinkedHashMap Constructors

import java.util.*;
public class LinkedHashMapConstructors {
public static void main(String[] args) {
// 1. Default constructor - insertion order
LinkedHashMap<String, Integer> map1 = new LinkedHashMap<>();
map1.put("A", 1);
map1.put("B", 2);
map1.put("C", 3);
System.out.println("Default (Insertion Order): " + map1.keySet());
// 2. With initial capacity
LinkedHashMap<String, Integer> map2 = new LinkedHashMap<>(16);
map2.put("X", 10);
map2.put("Y", 20);
map2.put("Z", 30);
System.out.println("With Capacity: " + map2.keySet());
// 3. With initial capacity and load factor
LinkedHashMap<String, Integer> map3 = new LinkedHashMap<>(16, 0.75f);
map3.put("P", 100);
map3.put("Q", 200);
map3.put("R", 300);
System.out.println("With Capacity & Load Factor: " + map3.keySet());
// 4. With initial capacity, load factor, and access order
LinkedHashMap<String, Integer> map4 = new LinkedHashMap<>(
16, 0.75f, true); // true for access order
map4.put("One", 1);
map4.put("Two", 2);
map4.put("Three", 3);
System.out.println("Before access: " + map4.keySet());
// Access some elements to change order
map4.get("One");
map4.get("Two");
System.out.println("After access: " + map4.keySet());
// 5. From existing map
Map<String, Integer> existingMap = new HashMap<>();
existingMap.put("First", 1);
existingMap.put("Second", 2);
existingMap.put("Third", 3);
LinkedHashMap<String, Integer> map5 = new LinkedHashMap<>(existingMap);
System.out.println("From existing map: " + map5.keySet());
}
}

Insertion Order vs Access Order

Insertion Order (Default)

import java.util.*;
public class InsertionOrderExample {
public static void main(String[] args) {
LinkedHashMap<String, String> insertionOrderMap = new LinkedHashMap<>();
insertionOrderMap.put("Z", "Zebra");
insertionOrderMap.put("A", "Apple");
insertionOrderMap.put("M", "Monkey");
insertionOrderMap.put("B", "Banana");
System.out.println("Insertion Order Map:");
insertionOrderMap.forEach((k, v) -> System.out.println(k + " -> " + v));
// Even after modifications, insertion order is maintained
insertionOrderMap.remove("M");
insertionOrderMap.put("C", "Cat");
insertionOrderMap.put("M", "Modified Monkey"); // Re-insert
System.out.println("\nAfter modifications:");
insertionOrderMap.forEach((k, v) -> System.out.println(k + " -> " + v));
}
}

Output:

Insertion Order Map:
Z -> Zebra
A -> Apple
M -> Monkey
B -> Banana
After modifications:
Z -> Zebra
A -> Apple
B -> Banana
C -> Cat
M -> Modified Monkey

Access Order Mode

import java.util.*;
public class AccessOrderExample {
public static void main(String[] args) {
// Third parameter 'true' enables access order
LinkedHashMap<String, String> accessOrderMap = new LinkedHashMap<>(
16, 0.75f, true);
accessOrderMap.put("A", "Apple");
accessOrderMap.put("B", "Banana");
accessOrderMap.put("C", "Cherry");
accessOrderMap.put("D", "Date");
System.out.println("Initial order: " + accessOrderMap.keySet());
// Access some elements
accessOrderMap.get("B"); // Access B
System.out.println("After accessing B: " + accessOrderMap.keySet());
accessOrderMap.get("A"); // Access A
System.out.println("After accessing A: " + accessOrderMap.keySet());
accessOrderMap.put("E", "Elderberry"); // Put also counts as access
System.out.println("After adding E: " + accessOrderMap.keySet());
accessOrderMap.get("C"); // Access C
System.out.println("After accessing C: " + accessOrderMap.keySet());
}
}

Output:

Initial order: [A, B, C, D]
After accessing B: [A, C, D, B]
After accessing A: [C, D, B, A]
After adding E: [C, D, B, A, E]
After accessing C: [D, B, A, E, C]

Real-World Use Cases

1. LRU (Least Recently Used) Cache

import java.util.*;
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true); // Access order = true
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
public void displayCache() {
System.out.println("Cache contents (most recent first):");
List<K> keys = new ArrayList<>(keySet());
Collections.reverse(keys); // Reverse to show most recent first
for (K key : keys) {
System.out.println(key + " -> " + get(key));
}
System.out.println();
}
}
public class LRUCacheExample {
public static void main(String[] args) {
LRUCache<String, String> cache = new LRUCache<>(3);
cache.put("User1", "Data1");
cache.put("User2", "Data2");
cache.put("User3", "Data3");
cache.displayCache();
cache.get("User1"); // Access User1 - makes it most recent
cache.displayCache();
cache.put("User4", "Data4"); // This will remove eldest entry (User2)
cache.displayCache();
cache.put("User5", "Data5"); // This will remove eldest entry (User3)
cache.displayCache();
}
}

Output:

Cache contents (most recent first):
User3 -> Data3
User2 -> Data2
User1 -> Data1
Cache contents (most recent first):
User1 -> Data1
User3 -> Data3
User2 -> Data2
Cache contents (most recent first):
User4 -> Data4
User1 -> Data1
User3 -> Data3
Cache contents (most recent first):
User5 -> Data5
User4 -> Data4
User1 -> Data1

2. Maintaining Insertion Order for Configuration

import java.util.*;
public class ConfigurationManager {
private LinkedHashMap<String, String> config = new LinkedHashMap<>();
public void setConfig(String key, String value) {
config.put(key, value);
}
public void displayConfigInOrder() {
System.out.println("Configuration (in setting order):");
config.forEach((key, value) -> System.out.println(key + " = " + value));
}
public static void main(String[] args) {
ConfigurationManager manager = new ConfigurationManager();
// Set configuration in specific order
manager.setConfig("database.host", "localhost");
manager.setConfig("database.port", "5432");
manager.setConfig("app.name", "MyApp");
manager.setConfig("app.version", "1.0.0");
manager.setConfig("logging.level", "DEBUG");
manager.displayConfigInOrder();
// Update existing config - order remains based on initial insertion
manager.setConfig("database.port", "3306");
manager.setConfig("app.name", "MyUpdatedApp");
System.out.println("\nAfter updates:");
manager.displayConfigInOrder();
}
}

3. Recent Items Tracker

import java.util.*;
class RecentItemsTracker<K> {
private LinkedHashMap<K, Long> recentItems;
private final int maxSize;
public RecentItemsTracker(int maxSize) {
this.maxSize = maxSize;
this.recentItems = new LinkedHashMap<K, Long>(maxSize, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, Long> eldest) {
return size() > maxSize;
}
};
}
public void addItem(K item) {
recentItems.put(item, System.currentTimeMillis());
}
public void accessItem(K item) {
if (recentItems.containsKey(item)) {
recentItems.get(item); // Just access to update order
}
}
public List<K> getRecentItems() {
return new ArrayList<>(recentItems.keySet());
}
public List<K> getRecentItemsMostRecentFirst() {
List<K> items = new ArrayList<>(recentItems.keySet());
Collections.reverse(items);
return items;
}
}
public class RecentItemsExample {
public static void main(String[] args) {
RecentItemsTracker<String> tracker = new RecentItemsTracker<>(5);
tracker.addItem("Item1");
tracker.addItem("Item2");
tracker.addItem("Item3");
tracker.addItem("Item4");
tracker.addItem("Item5");
System.out.println("Initial items: " + tracker.getRecentItems());
tracker.accessItem("Item2"); // Make Item2 most recent
System.out.println("After accessing Item2: " + tracker.getRecentItems());
tracker.addItem("Item6"); // This should remove Item1 (eldest)
System.out.println("After adding Item6: " + tracker.getRecentItems());
System.out.println("Most recent first: " + tracker.getRecentItemsMostRecentFirst());
}
}

Advanced Operations

Iteration Order Preservation

import java.util.*;
public class IterationOrderExample {
public static void main(String[] args) {
LinkedHashMap<Integer, String> orderedMap = new LinkedHashMap<>();
// Add elements in specific order
for (int i = 10; i > 0; i--) {
orderedMap.put(i, "Value" + i);
}
System.out.println("Iteration order matches insertion order:");
// Entry set iteration
System.out.println("Entry Set:");
for (Map.Entry<Integer, String> entry : orderedMap.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// Key set iteration
System.out.println("\nKey Set:");
for (Integer key : orderedMap.keySet()) {
System.out.println(key);
}
// Values iteration
System.out.println("\nValues:");
for (String value : orderedMap.values()) {
System.out.println(value);
}
}
}

Copy Preservation

import java.util.*;
public class CopyPreservationExample {
public static void main(String[] args) {
LinkedHashMap<String, Integer> original = new LinkedHashMap<>();
original.put("First", 1);
original.put("Second", 2);
original.put("Third", 3);
original.put("Fourth", 4);
System.out.println("Original: " + original.keySet());
// Copy constructor preserves order
LinkedHashMap<String, Integer> copy = new LinkedHashMap<>(original);
System.out.println("Copy: " + copy.keySet());
// PutAll preserves order
LinkedHashMap<String, Integer> anotherCopy = new LinkedHashMap<>();
anotherCopy.putAll(original);
System.out.println("PutAll Copy: " + anotherCopy.keySet());
// Sub operations may not preserve full order in some Java versions
LinkedHashMap<String, Integer> subMap = new LinkedHashMap<>();
original.entrySet().stream()
.limit(2)
.forEach(entry -> subMap.put(entry.getKey(), entry.getValue()));
System.out.println("Sub Map: " + subMap.keySet());
}
}

Performance Characteristics

import java.util.*;
public class PerformanceComparison {
public static void main(String[] args) {
int size = 100000;
// HashMap performance
long startTime = System.currentTimeMillis();
Map<Integer, String> hashMap = new HashMap<>();
for (int i = 0; i < size; i++) {
hashMap.put(i, "Value" + i);
}
long hashMapTime = System.currentTimeMillis() - startTime;
// LinkedHashMap performance (insertion order)
startTime = System.currentTimeMillis();
Map<Integer, String> linkedHashMap = new LinkedHashMap<>();
for (int i = 0; i < size; i++) {
linkedHashMap.put(i, "Value" + i);
}
long linkedHashMapTime = System.currentTimeMillis() - startTime;
// LinkedHashMap performance (access order)
startTime = System.currentTimeMillis();
Map<Integer, String> accessOrderMap = new LinkedHashMap<>(16, 0.75f, true);
for (int i = 0; i < size; i++) {
accessOrderMap.put(i, "Value" + i);
}
long accessOrderTime = System.currentTimeMillis() - startTime;
System.out.println("HashMap put time: " + hashMapTime + "ms");
System.out.println("LinkedHashMap (insertion) put time: " + linkedHashMapTime + "ms");
System.out.println("LinkedHashMap (access) put time: " + accessOrderTime + "ms");
// Iteration performance
startTime = System.currentTimeMillis();
for (String value : hashMap.values()) {
// Just iterating
}
long hashMapIteration = System.currentTimeMillis() - startTime;
startTime = System.currentTimeMillis();
for (String value : linkedHashMap.values()) {
// Just iterating
}
long linkedHashMapIteration = System.currentTimeMillis() - startTime;
System.out.println("\nHashMap iteration time: " + hashMapIteration + "ms");
System.out.println("LinkedHashMap iteration time: " + linkedHashMapIteration + "ms");
}
}

Key Points Summary

  1. Insertion Order: Default mode, maintains order in which entries were added
  2. Access Order: When enabled (third constructor parameter = true), maintains order based on access pattern
  3. Performance: Slightly slower than HashMap due to maintaining linked list
  4. Memory: Uses more memory than HashMap due to linked list overhead
  5. Thread Safety: Not synchronized - use Collections.synchronizedMap() if needed
  6. Use Cases:
  • LRU Caches
  • Maintaining configuration order
  • Recent items tracking
  • Any scenario where iteration order matters

Best Practices

import java.util.*;
public class LinkedHashMapBestPractices {
public static void main(String[] args) {
// 1. Use appropriate initial capacity
LinkedHashMap<String, String> map1 = new LinkedHashMap<>(100);
// 2. Use access order for LRU caches
LinkedHashMap<String, String> lruCache = new LinkedHashMap<>(
16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return size() > 1000;
}
};
// 3. For thread safety
Map<String, String> synchronizedMap = 
Collections.synchronizedMap(new LinkedHashMap<>());
// 4. Preserve order during copy
LinkedHashMap<String, Integer> original = new LinkedHashMap<>();
original.put("A", 1);
original.put("B", 2);
LinkedHashMap<String, Integer> copy = new LinkedHashMap<>(original);
// 5. Iterate efficiently
for (Map.Entry<String, Integer> entry : copy.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}

LinkedHashMap is the perfect choice when you need HashMap's performance characteristics combined with predictable iteration order, making it invaluable for many real-world applications.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper