LinkedHashMap 能直接支持 LRU 逻辑是因为启用 accessOrder=true 后,get/put 操作将节点移至尾部,最久未用者位于头部;需重写 removeEldestEntry() 在 put 后判断并移除头部节点,且注意 null key 和初始化容量的误区。

为什么 LinkedHashMap 能直接支持 LRU 逻辑
因为 LinkedHashMap 的构造函数允许开启访问顺序模式(accessOrder = true),此时每次 get() 或 put() 都会把对应节点移到链表尾部,而最久未使用的元素自然留在头部——这正是 LRU “淘汰头部” 的核心前提。
但注意:默认构造的 LinkedHashMap 是插入顺序(accessOrder = false),不满足 LRU;必须显式传参启用访问顺序。
如何在 get/put 后自动触发淘汰
不能只靠 LinkedHashMap 自身行为,它不会主动删数据。需要重写 removeEldestEntry() 方法,在每次结构变更后由框架回调判断是否该删最老项。
-
removeEldestEntry()返回true时,LinkedHashMap会立即移除链表头部节点 - 通常用
size() > capacity判断,但也可以加时间戳、引用计数等自定义逻辑 - 这个方法只在
put()、putAll()后调用,get()不触发——所以容量控制只发生在写入路径,读多写少场景下很安全
一个可直接运行的 LRU 缓存实现
下面是最简可行版本,封装了泛型和固定容量:
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;
}
}</k></k></k>
使用时:LRUCache,之后调用 put() 和 get() 就自动具备 LRU 行为。
注意:这个类不是线程安全的,高并发需包装 Collections.synchronizedMap() 或改用 ConcurrentHashMap + 手动维护顺序。
容易被忽略的边界问题
LinkedHashMap 的 removeEldestEntry() 在 put(null, value) 时仍会执行,但 eldest.getKey() 可能为 null——如果里面写了非空校验会 NPE;另外,如果缓存 key 本身允许 null,那 size 判断前最好先确认 eldest != null。
还有:初始化时传的初始容量(第一个参数)只是哈希表桶数组大小,不影响 LRU 容量上限;真正起作用的是你在 removeEldestEntry() 里写的 capacity 值。










