HashMap vs LinkedHashMap
LinkedHashMap is a HashMap plus a doubly linked list running through all its entries. That list gives it a predictable iteration order:
- Insertion order (the default): entries come out in the order they were first put. Re-putting an existing key doesn't move it.
- Access order (
new LinkedHashMap<>(16, 0.75f, true)): everygetorputmoves the entry to the end, so the first entry is always the least recently used. Combined withremoveEldestEntry, that's an LRU cache in a few lines.
Everything else is HashMap: O(1) operations, one null key, not thread-safe. It costs a little more memory (two extra pointers per entry) and is slightly slower to change.
Use LinkedHashMap when order matters: JSON output that should keep field order, "recently viewed" lists, caches. Use HashMap when it doesn't.
Side by side
| HashMap | LinkedHashMap | |
|---|---|---|
| Iteration order | Unpredictable (can change on resize) | Insertion order, or access order |
| Internals | Hash table | Hash table + doubly linked list through entries |
| get / put | O(1) | O(1), slightly more work |
| Memory | Less | Two extra pointers per entry |
| Iterating | Visits every bucket (proportional to capacity) | Follows the list (proportional to size) |
| LRU cache | No | Yes: access order + removeEldestEntry |
| Java 21 sequenced methods | No | firstEntry, lastEntry, pollFirstEntry, putFirst, reversed |
| Use it for | Order doesn't matter | Order matters, caches |
Example
Map<String, Integer> hash = new HashMap<>();
Map<String, Integer> linked = new LinkedHashMap<>();
for (String k : List.of("mango", "apple", "zebra")) { hash.put(k, 1); linked.put(k, 1); }
System.out.println(hash.keySet()); // [zebra, apple, mango] order comes from the hash codes
System.out.println(linked.keySet()); // [mango, apple, zebra] insertion orderclass LruCache<K, V> extends LinkedHashMap<K, V> {
private final int max;
LruCache(int max) { super(16, 0.75f, true); this.max = max; } // true = access order
@Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > max; // evict the least recently used
}
}
LruCache<String, String> cache = new LruCache<>(2);
cache.put("a", "A"); cache.put("b", "B");
cache.get("a"); // "a" is now the most recently used
cache.put("c", "C"); // evicts "b"
System.out.println(cache.keySet()); // [a, c]Common mistake
Relying on a HashMap's iteration order. It depends on hash codes and capacity and can change when the map grows or between Java versions.
Under the hood
Iterating a HashMap walks every bucket, including empty ones, so a huge, mostly empty HashMap iterates slowly; a LinkedHashMap follows its list and only visits real entries. For caches in production, prefer a library such as Caffeine (size and time limits, statistics, concurrency), but the LinkedHashMap LRU is perfect for small, single-threaded caches and interviews.
Check yourself
With accessOrder = true, what does get(key) do to the entry?
How this connects
Where this leads
You've reached the end of this thread. Try a learning path for what's next.
Part of Java 8 and collections, practically.
Was this lesson helpful?
Finished reading? Mark it complete to track your progress.