LinkedHashMap 的原理和应用?

2025年 阅读约 7 分钟 面试指南 · Java面试

深入解析LinkedHashMap的原理和应用,从插入顺序到LRU缓存实现,分初级、中级、高级三个层次全面讲解。

一句话总结

LinkedHashMap = HashMap + 双向链表,Entry 继承 Node 增加 before/after 指针。默认按插入顺序遍历,设置 accessOrder=true 按访问顺序(最近访问移到末尾)。重写 removeEldestEntry() 即可实现 LRU 缓存(超过容量自动删除最老元素),是面试高频手写题。

初级理解

LinkedHashMap 继承自 HashMap,在 HashMap 的基础上增加了双向链表来维护元素的插入顺序或访问顺序。

核心特点:拥有 HashMap 的所有特性(O(1) 查询),同时可以按插入顺序或访问顺序遍历。

// 默认按插入顺序 LinkedHashMap<String, Integer> map = new LinkedHashMap<>(); map.put("c", 3); map.put("a", 1); map.put("b", 2); // 遍历顺序 = 插入顺序 map.forEach((k, v) -> System.out.print(k)); // c a b

与 HashMap 的区别:HashMap 遍历顺序不可预测;LinkedHashMap 遍历顺序可预测(插入顺序或访问顺序)。

中级深入

底层数据结构:LinkedHashMap 的 Entry 继承 HashMap.Node,额外增加了 before 和 after 两个指针,形成双向链表。

// LinkedHashMap.Entry 结构 static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; // 双向链表指针 }

两种排序模式:

1. 插入顺序(默认):accessOrder = false,遍历顺序 = 插入顺序

2. 访问顺序:accessOrder = true,最近访问的元素移到链表末尾,遍历顺序 = 访问顺序(从旧到新)

// 访问顺序模式 LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true); map.put("a", 1); map.put("b", 2); map.put("c", 3); map.get("a"); // 访问 a,a 移到末尾 map.forEach((k, v) -> System.out.print(k)); // b c a

高级拓展

LRU 缓存实现:利用 LinkedHashMap 的访问顺序 + removeEldestEntry 方法,可以轻松实现 LRU(Least Recently Used)缓存。

// 最简单的 LRU 缓存实现 class LRUCache<K, V> extends LinkedHashMap<K, V> { private final int capacity; public LRUCache(int capacity) { super(capacity, 0.75f, true); // accessOrder = true this.capacity = capacity; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; // 超过容量自动删除最老的元素 } } LRUCache<String, String> cache = new LRUCache<>(3); cache.put("a", "1"); cache.put("b", "2"); cache.put("c", "3"); cache.get("a"); // 访问 a cache.put("d", "4"); // 超过容量,删除最老的 b // 此时缓存:c, a, d

LinkedHashMap 在 Spring 中的应用:Spring 的 LinkedCaseInsensitiveMap(不区分大小写的 Map)就是继承 LinkedHashMap 实现的。

面试加分项:能手写 LRU 缓存实现(基于 LinkedHashMap),是高频面试题。

实战场景

场景:LRU 缓存完整实现(线程安全版)

// 线程安全的 LRU 缓存 public class ConcurrentLRUCache<K, V> { private final int capacity; private final LinkedHashMap<K, V> map; public ConcurrentLRUCache(int capacity) { this.capacity = capacity; this.map = new LinkedHashMap<K, V>(capacity, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; } }; } public synchronized V get(K key) { return map.get(key); } public synchronized void put(K key, V value) { map.put(key, value); } public synchronized int size() { return map.size(); } }

面试模拟

Q:LinkedHashMap 和 HashMap 有什么区别?

A:HashMap 遍历顺序不可预测;LinkedHashMap 通过双向链表维护顺序,默认按插入顺序,也可按访问顺序。代价是每个 Entry 多了两个指针(before/after),内存稍高,但操作复杂度不变(O(1))。

Q:如何用 LinkedHashMap 实现 LRU 缓存?

A:1) 构造时设置 accessOrder=true(访问顺序);2) 重写 removeEldestEntry(),当 size() > capacity 时返回 true,自动删除最老的条目。LinkedHashMap 的 afterNodeInsertion() 在每次 put 后调用 removeEldestEntry() 检查。