removeeldestentry在每次put或putall成功插入新元素后、返回前被调用一次,仅由写操作触发,读操作不触发;需显式重写返回true才能启用自动清理,常用于实现lru缓存等场景。

removeEldestEntry 方法什么时候会被触发
它不是定时器,也不是后台线程,而是在每次 put 或 putAll 成功插入新元素后、返回前被调用一次。也就是说,只有写操作才会检查要不要淘汰——读操作(get)完全不触发它。
常见错误现象:removeEldestEntry 从不执行?大概率是因为你只做 get,或者用了 putIfAbsent 但 key 已存在(此时不会插入,也不会触发钩子)。
使用场景:实现 LRU 缓存、固定容量的最近访问记录、带过期策略的内存缓存(需配合时间戳字段)。
怎么正确重写 removeEldestEntry 返回 true
默认返回 false,必须显式重写才能启用自动清理。核心逻辑是判断“最老 entry 是否该删”,而不是“当前 size 是否超限”——因为 eldest 是 LinkedHashMap 内部双向链表头节点(即最早插入且未被访问过的那个),它可能比你想象中“老”得多。
实操建议:
- 不要直接比较
size() > MAX_SIZE,而是用size() > MAX_SIZE作为条件之一,否则首次插入就可能误删 - 如果要做「访问时更新顺序」的 LRU,必须确保构造时传入
true的accessOrder参数,否则eldest始终是插入顺序最老的,不是最近最少使用的 - 想实现 TTL(如 5 分钟过期),得在
eldest对应的 value 里存时间戳,并在removeEldestEntry中判断System.currentTimeMillis() - timestamp > 5 * 60 * 1000
示例(固定容量 LRU):
new LinkedHashMap<String, String>(16, 0.75f, true) {
private static final int MAX_SIZE = 100;
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return size() > MAX_SIZE;
}
};为什么明明 size 超了却没删掉最老项
典型原因是没搞清 accessOrder 和 removeEldestEntry 的协作关系。当 accessOrder = false(默认),eldest 是插入最早的;但如果你频繁 get 某些 key,它们不会移动到链表尾,所以 eldest 可能长期不变——看起来“卡住”了。
另一个坑:继承自 LinkedHashMap 的子类若重写了 put 却没调用 super.put,钩子就彻底失效。
性能影响:每次 put 都调用一次该方法,开销极小(只是个布尔判断),但若你在里面做耗时操作(比如 IO、复杂计算),会拖慢所有写入。
removeEldestEntry 和 WeakReference / SoftReference 能否混用
不能自动协同。LinkedHashMap 不管 value 是强引用还是软引用;removeEldestEntry 删除的是整个 Map.Entry,包括 key 和 value 的强引用。如果你希望 value 被 GC 回收得更早,得自己封装 value 为 SoftReference<v></v>,但要注意:软引用对象被回收后,value 变成 null,你需要在 get 时检查并清理空 entry(LinkedHashMap 不自动做这事)。
兼容性注意:Java 8+ 行为一致,但 Android 低版本(如 API 21 前)有 bug,removeEldestEntry 在某些并发场景下可能被跳过——务必在目标环境中验证。
容易被忽略的一点:这个钩子只对新插入生效。如果你用 replace(key, oldValue, newValue) 或 computeIfPresent 更新已有 key,不会触发它——哪怕更新后整体 size 没变,也跟淘汰无关。










