利用linkedhashmap的removeeldestentry方法实现固定大小lru缓存,需启用访问顺序模式(构造时传true),重写该方法在put后判断size()是否超容并返回true以自动淘汰链表头最老项。

利用 LinkedHashMap 的 removeEldestEntry 方法实现固定大小缓存,本质是借助其“访问顺序 + 可扩展钩子”的特性,在插入新元素时自动淘汰最老(即最先插入或最少访问)的条目。关键在于重写该方法并启用访问顺序模式。
启用访问顺序模式
LinkedHashMap 默认按插入顺序维护链表,但缓存通常需要最近最少使用(LRU)策略,这就要求它按访问顺序排列。需在构造时传入 true 作为第三个参数:
new LinkedHashMap<k v>(initialCapacity, loadFactor, true)</k>
启用后,每次 get() 或 put() 都会把对应节点移到链表尾部,链表头部就始终是最久未被访问的项。
重写 removeEldestEntry 控制淘汰逻辑
该方法在每次 put()(或 putAll())后被调用,接收当前刚插入的 Entry 作为参数,返回 true 则删除链表头部(最老)的条目。
要实现固定容量缓存,只需判断当前 size 是否超过阈值:
@Override
protected boolean removeEldestEntry(Map.Entry<k v> eldest) {
return size() > MAX_CAPACITY;
}</k>
-
注意:不要在方法里手动调用
remove()或修改 map,否则可能引发并发或状态不一致问题 -
时机:它只在
put后触发,所以get不会引发淘汰,符合 LRU 行为 -
线程安全:
LinkedHashMap本身非线程安全,如需多线程使用,应包装为Collections.synchronizedMap()或用ConcurrentHashMap+ 手动 LRU 管理(但会失去内置钩子优势)
一个完整可运行的 LRU 缓存示例
以下是一个轻量、线程安全(加了同步)、带泛型的固定容量缓存封装:
public class LRUCache<k v> extends LinkedHashMap<k v> {
private final int capacity;
public LRUCache(int capacity) {
// 初始容量、负载因子、启用访问顺序
super(16, 0.75f, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<k v> eldest) {
return size() > capacity;
}
// 可选:覆盖 get,确保 null 值也能正确体现“命中”
@Override
public V get(Object key) {
return super.get(key);
}
}</k></k></k>
使用方式简洁:
LRUCache<string integer> cache = new LRUCache(3);
cache.put("a", 1);
cache.put("b", 2);
cache.put("c", 3); // size=3
cache.put("d", 4); // 自动移除 "a",size 保持为 3
System.out.println(cache); // {b=2, c=3, d=4}
</string>
注意事项与常见误区
-
不要在 removeEldestEntry 中抛异常或做耗时操作:它在
put内部同步执行,会影响写性能 - size() 返回的是当前键值对数量,不是容量上限;淘汰发生在插入后检查,因此最大 size 恒等于设定容量
-
如果需要支持“读写都刷新顺序”,必须启用 accessOrder=true;否则即使调用
get(),顺序也不变,退化为 FIFO -
key 为 null 是允许的,但要注意
LinkedHashMap对 null key 的处理和哈希一致性
不复杂但容易忽略细节,掌握访问顺序开关和钩子触发时机,就能零依赖写出高效 LRU 缓存。










