LinkedHashMap 实现 LRU 缓存
面试官问:"LinkedHashMap 是怎么实现的?"
候选人小王答:"它是 HashMap 的子类,比 HashMap 多维护了一个双向链表。"
面试官追问:"那怎么用 LinkedHashMap 实现 LRU 缓存?"
小王支支吾吾答不上来。
【面试官心理】 LinkedHashMap 是面试常考的进阶知识点。能说清楚它继承 HashMap、实现双向链表、维护访问顺序的候选人,说明对集合框架有过源码级别的研究。能手写 LRU 缓存的,基本是 P6+ 了。
一、LinkedHashMap 核心原理 🔴
1.1 继承关系
1.2 Entry 节点结构
1.3 与 HashMap 的关系
LinkedHashMap 继承 HashMap,重用了 HashMap 的数组 + 链表/红黑树结构,同时添加了维护插入/访问顺序的双向链表。
二、accessOrder 模式 🟡
2.1 两种模式
2.2 accessOrder 的作用
2.3 访问顺序示例
三、实现 LRU 缓存 🔴
3.1 LRU 缓存原理
LRU = Least Recently Used(最近最少使用)
3.2 removeEldestEntry() 方法
3.3 完整 LRU 缓存实现
3.4 ❌ 错误示范
候选人原话:"LinkedHashMap 是按插入顺序的,没办法实现 LRU。"
问题诊断:
- 不知道 accessOrder 参数的作用
- 没有看过 LinkedHashMap 的源码
面试官内心 OS:"这个候选人可能用过 LinkedHashMap,但没有深入理解它的实现。"
【面试官心理】
LinkedHashMap 实现 LRU 缓存是经典面试题。关键是 accessOrder = true 和 removeEldestEntry() 方法。能完整说出实现思路并写出代码的候选人,说明真正理解了 LinkedHashMap 的设计。
四、LRU 缓存的线程安全 🟡
4.1 为什么不直接用 LinkedHashMap
4.2 Collections.synchronizedMap
4.3 推荐方案
五、LinkedHashMap 的应用场景 🟡
5.1 保持插入顺序
5.2 实现 FIFO 缓存
5.3 记录访问顺序
六、源码解析 🟡
6.1 关键方法重写
6.2 遍历的优势
七、生产避坑清单 🟡
7.1 ❌ 常见错误
7.2 性能注意事项
7.3 与 HashMap 的内存对比
LinkedHashMap 适合需要维护元素顺序的场景(如缓存、LRU),但如果不需要顺序,用 HashMap 更省内存。
八、面试追问链 🟡
8.1 第一层追问
面试官:"LinkedHashMap 和 HashMap 的区别是什么?"
候选人:...
正确回答:
- LinkedHashMap 继承 HashMap
- LinkedHashMap 额外维护双向链表,记录插入/访问顺序
- LinkedHashMap 支持按插入顺序或访问顺序遍历
8.2 第二层追问
面试官:"accessOrder 参数是什么意思?"
候选人:...
正确回答:
accessOrder = false:按插入顺序排列(默认)accessOrder = true:按访问顺序排列,get()和put()都会把元素移到链表末尾
8.3 第三层追问
面试官:"怎么用 LinkedHashMap 实现 LRU 缓存?"
候选人:...
正确回答:
- 设置
accessOrder = true - 重写
removeEldestEntry()方法,返回size() > maxCapacity
8.4 第四层追问
面试官:"LinkedHashMap 是线程安全的吗?"
候选人:...
正确回答:不是。线程安全版本需要外部同步,如 Collections.synchronizedMap() 或使用 ConcurrentHashMap + 额外同步。
【学习小结】 LinkedHashMap 核心要点:
- 继承 HashMap,额外维护双向链表
accessOrder = true时按访问顺序排列- 通过
removeEldestEntry()实现缓存淘汰 - 重写
newNode()、afterNodeAccess()、afterNodeRemoval()维护链表 - 不是线程安全的