java中高效实现lru缓存的核心是利用linkedhashmap的accessorder=true特性及重写removeeldestentry方法,时间复杂度o(1);需注意put触发淘汰、key的equals/hashcode正确性、内存泄漏防范等细节。

Java 中实现高效 LRU 缓存,核心是利用 LinkedHashMap 的访问顺序特性,配合重写 removeEldestEntry 方法,时间复杂度稳定在 O(1)。
用 LinkedHashMap 实现最简高效版
LinkedHashMap 内部维护双向链表,支持按插入顺序或访问顺序排列。启用访问顺序(accessOrder = true)后,每次 get 或 put 都会将对应节点移到链表尾部,天然符合 LRU “最近使用置尾、淘汰头节点” 的逻辑。
- 构造时传入
initialCapacity、loadFactor和true(表示 accessOrder) - 重写
removeEldestEntry:当 map size 超过容量时返回true,触发自动删除最老项(即链表头) - 无需手动维护链表或哈希表,JDK 已封装好线程不安全但高性能的底层操作
保证线程安全的常见做法
若需多线程环境使用,不建议直接加 synchronized 包裹整个 map(会严重降低并发性能)。推荐以下方式:
- 使用
Collections.synchronizedMap(new LinkedHashMap(...))—— 粗粒度同步,适合低并发场景 - 自行封装,对
get和put分别加锁(如读写锁),但需注意避免死锁和 double-check 问题 - 更优解:改用
ConcurrentHashMap+ 自定义队列(如ConcurrentLinkedQueue)+ 原子计数器,但实现复杂度上升,仅在高并发且严格要求吞吐量时考虑
手动实现(理解原理用)
若想深入理解或定制行为(如带过期、异步淘汰),可基于 HashMap + Doubly Linked List 手写:
- 用
HashMap<k node></k>实现 O(1) 查找 - 每个
Node含前后指针,维护双向链表;head指向最久未用,tail指向最新使用 -
get时查 map → 移动 node 到 tail → 返回 value -
put时若存在则更新值并移至 tail;若不存在且超容,先删 head → 插入新 node 到 tail
注意边界与优化细节
实际使用中容易忽略的关键点:
-
removeEldestEntry在put后触发,不是get后 —— 所以容量限制只影响写入,读不会导致淘汰 - key 必须正确实现
equals和hashCode,否则get可能失效 - 避免缓存大对象或未限制生命周期的对象,防止内存泄漏;必要时用
WeakReference<v></v>包装 value - 调试时可覆写
afterNodeInsertion或afterNodeAccess(JDK 8+)做日志跟踪
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











