LRU缓存通过LinkedHashMap的accessOrder=true实现访问序维护,get/put自动移至尾部,重写removeEldestEntry判断size>capacity触发头节点淘汰;非线程安全,多线程需加锁或选用Guava/Caffeine。

LRU(Least Recently Used)缓存淘汰算法的核心是:当缓存满时,优先剔除最久未被访问的元素。Java 中 LinkedHashMap 天然支持按访问顺序维护键值对,配合重写 removeEldestEntry 方法,可几行代码实现高性能 LRU 缓存。
利用 accessOrder=true 构建访问序链表
LinkedHashMap 构造函数支持传入 accessOrder 参数。设为 true 时,每次 get() 或 put() 都会把对应节点移到链表尾部,链表头部自然就是最久未使用的项。
- 默认
accessOrder=false(插入序),不满足 LRU 要求 - 必须显式指定
new LinkedHashMap(capacity, 0.75f, true) - 注意:
get()触发重排序的前提是该 key 已存在;新插入的 key 总是加在尾部
重写 removeEldestEntry 控制自动淘汰
该方法在每次 put() 后被调用,返回 true 则自动删除链表头节点(即最老项)。只需在此判断当前 size 是否超过容量上限:
- 不要在
put()外手动检查 size 并调用remove(),易出错且破坏原子性 - 推荐写法:
return size() > capacity; - 若需支持“仅读操作触发淘汰”,可扩展逻辑(如记录最后访问时间戳),但标准 LRU 不需要
线程安全需额外包装
LinkedHashMap 本身非线程安全。若多线程并发读写,有几种稳妥选择:
- 使用
Collections.synchronizedMap(new LinkedHashMap(...)),简单但全局锁粒度大 - 用
ConcurrentHashMap+ 显式维护访问顺序(较复杂,失去原生优势) - 更推荐:封装成类,内部用
ReentrantLock细粒度控制get/put,或直接选用guava Cache/Caffeine
完整轻量实现示例
以下是一个线程不安全但简洁可用的模板(生产环境建议加锁):
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
// accessOrder = true
super(capacity, 0.75f, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
}
初始化后即可像普通 Map 使用:cache.put(k, v)、cache.get(k),满容时自动踢出最老项。

















