登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  java教程

Java LinkedHashMap 怎样用 accessOrder 实现最近最少使用淘汰

来源:17golang原创

时间:2026-10-18 13:34:11 115浏览 收藏

Java 的轻量 LRU 缓存可以直接交给 LinkedHashMap 维护:用三参数构造器把 accessOrder 设为 true,再覆写 removeEldestEntry,让超过容量上限的新条目触发最久未访问项淘汰。这个方案适合单线程,或已经由外部锁保护的缓存;LinkedHashMap 本身不是并发容器。

官方地址:https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/LinkedHashMap.html

要点速览
  • false 是插入顺序,true 才是从最久访问到最近访问的顺序。
  • get、put、compute 等操作可能改变访问顺序,调试读取也不能当成纯查询。
  • removeEldestEntry 通常在新映射加入后判断,容量条件应写成 size() > maxEntries。

accessOrder=true 如何把访问变成顺序变化

普通的 LinkedHashMap 记录插入顺序;三参数构造器的最后一个参数决定是否改成访问顺序。设为 true 后,命中一个已有键,键值对会移到最近访问端,队列另一端就是最久未访问项。

import java.util.LinkedHashMap;

var cache = new LinkedHashMap(16, 0.75f, true); // true 表示按访问顺序维护
cache.put("A", "alpha");
cache.put("B", "bravo");
cache.put("C", "charlie");
cache.get("A"); // 命中后 A 移到最近访问端,顺序近似为 B、C、A

// entrySet() 只用于观察当前顺序;访问有序 map 时仍要注意并发修改。
System.out.println(cache.keySet());

上例的顺序变化是这个 API 的核心,而不是 get 返回值本身。putIfAbsent、compute、computeIfAbsent、computeIfPresent、merge 等也可能产生访问;只有“看起来像读取”的调用,仍可能改变迭代顺序。

Java LinkedHashMap accessOrder true 与 get 后访问顺序移动的静态说明图
图1:LinkedHashMap 访问顺序说明图,展示 get 后条目移动到最近访问端;这是静态结构图,不是运行截图。

removeEldestEntry 怎样完成容量淘汰

把淘汰规则放进一个专用子类,调用方只负责 get 和 put。当新映射加入后,removeEldestEntry 返回 true,最旧条目就会被移除。条件不能写成 >=,否则容量刚好达到上限时也会误删。

import java.util.LinkedHashMap;
import java.util.Map;

final class LruCache extends LinkedHashMap {
    private final int maxEntries;

    LruCache(int maxEntries) {
        super(maxEntries + 1, 0.75f, true); // true:按最近访问顺序排列
        if (maxEntries  eldest) {
        return size() > maxEntries; // 新条目进入后超过上限,淘汰最旧项
    }
}

如果容量为 3,依次放入 A、B、C 后不会淘汰;命中 A 再放入 D 时,B 会成为最久未访问项并被淘汰。这个行为只说明容器顺序和淘汰条件正确,不等于提供了过期时间、持久化或后台刷新能力。

Java LinkedHashMap removeEldestEntry 按容量淘汰最旧条目的静态关系图
图2:removeEldestEntry 容量淘汰关系图,展示判断点与最旧条目的边界;这是静态说明图,不是执行证据。

迁移时要看清哪些访问和线程边界

操作或场景对访问顺序的影响迁移提醒
get命中已有键时移动到最近端不要把它当作无副作用读操作
put、merge对已存在键可能形成访问以官方 API 语义为准,不凭方法名猜测
迭代器与并发访问顺序变化属于结构修改外部同步,并且不要依赖 fail-fast 保证正确性
淘汰回调返回 true 才移除 eldest只写轻量规则,不在回调里做网络或长事务

需要并发读写时,应先确定锁的所有权:可以让整个缓存方法使用同一把锁,也可以把缓存放在已有同步封装中。只把 get 加锁、把遍历或 put 留在锁外,会让顺序和容量判断失去一致性。读多写少也不能因此直接把它当成线程安全容器。

发布前的 LRU 检查清单

  • 容量参数大于 0,且构造器最后一个参数确实是 true。
  • 重复访问一个旧键后,再插入新键,淘汰的是新的 eldest,而不是最早插入项。
  • 重复 put、空缓存、容量为 1、访问不存在的键都单独覆盖。
  • 缓存值没有过期时间要求;若有 TTL、主动失效或统计需求,另加明确机制。
  • 并发场景统一保护读、写、迭代和淘汰观察,不把 fail-fast 异常当同步方案。

两个容易混淆的问题

把 accessOrder 改成 false 还能做 LRU 吗?

不能得到按访问淘汰的 LRU 语义。false 只记录插入顺序,最早插入项会被视为 eldest;它可以做 FIFO 风格容量限制,但不是最近最少使用。

removeEldestEntry 能异步清理过期缓存吗?

不能。它围绕映射加入后的容量判断工作,不负责定时扫描,也不会自动检查时间戳。要做 TTL,应把时间字段、过期判断和清理触发点设计出来,并继续保持并发边界清晰。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>