accessOrder = true 时,get() 通过 afterNodeAccess() 将命中节点移至链表尾部,使头节点成为最久未访问者;淘汰需重写 removeEldestEntry() 返回 size() > capacity,否则不触发删除。

因为 accessOrder = true 时,每次 get 或 put 都会把对应节点移到双向链表尾部,使最久未访问的节点始终在头部——这正是 LRU 淘汰逻辑所需的数据布局。
accessOrder = true 时,get() 怎么改变链表顺序?
启用 accessOrder 后,get() 不再只是查值,还会触发 afterNodeAccess() 方法,将命中的节点从原位置解链,再插入到链表尾部(tail)。
- 链表头(
head)永远是最久未访问的节点,适合淘汰 - 链表尾(
tail)永远是最近一次被get或put的节点 - 这个移动动作只发生在
accessOrder == true时;默认false下get完全不扰动顺序 - 注意:
getOrDefault()同样触发该行为,它底层也调用了getNode()+afterNodeAccess()
为什么必须重写 removeEldestEntry() 才能自动淘汰?
LinkedHashMap 本身不管理容量,accessOrder 只负责排序,不负责清理。真正执行淘汰动作的是 put() 流程中隐式调用的 removeEldestEntry()。
将 WCAG 2 AA 无障碍标准应用于设计系统、UI 组件和网页——通过可运行的验证器进行对比度计算,采用设计 token 模式使颜色天生可访问,设定排版和焦点状态规则,并提供语义检查清单。适用于构建或审查 UI。
- 该方法默认返回
false,即永不删除 - 你需要继承
LinkedHashMap并重写它,例如:return size() > capacity; - 这个判断在每次
put()后立即执行,所以新增条目前会先检查并可能移除head节点 - 别漏掉
put()本身也会触发节点移至尾部——所以新 entry 总在尾,旧 entry 留在头,淘汰逻辑才成立
accessOrder 对性能和线程安全的实际影响
开启 accessOrder 不仅改变语义,还带来可测量的开销和约束:
- 每次
get()多出两次指针操作(before/after解链+重连),比HashMap.get()慢约 10%~15% - 迭代顺序不再稳定:同一 key 多次
get后,它在entrySet()中的位置会变,不能依赖固定索引 - 不是线程安全的,即使只读迭代也需外部同步——因为
get()会修改链表结构 - 如果用作缓存,务必搭配合理的初始容量和装载因子(如
new LinkedHashMap(capacity, 0.75f, true)),避免频繁扩容打断链表维护
真正容易被忽略的是:accessOrder 只保证“访问后移尾”,但淘汰动作完全依赖你重写的 removeEldestEntry() 返回值;哪怕链表头确实是 LRU 候选,只要这个方法返回 false,就不会删——很多人卡在这一步,以为开了 true 就自动 LRU 了。

















