在开发里遇到"遍历顺序"这个需求,你大概率逃不过 LinkedHashSet 和 LinkedHashMap 这两个类。明明 HashMap 和 HashSet 已经够用了,为什么还要专门搞一套"能记住插入顺序"的变体?它们的顺序又是怎么维护的?用了这么久,我踩过不少坑,也读过源码,这篇文章就把这两个类讲透,包括内部原理、经典用法和常见坑点,一次聊明白。
1. LinkedHashSet/LinkedHashMap 到底解决什么问题:三个日常场景
1.1 场景一:HashMap 把插入顺序"打乱"了
先从一个很常见的例子说起。你在系统里维护了一个菜单列表,用户登录之后要先拿一级菜单,再拿二级菜单。需求文档上明确写着"按配置顺序展示",于是你很自然地写了一个 HashMap:
java复制Map<String, String> menuMap = new HashMap<>();
menuMap.put("首页", "/home");
menuMap.put("订单", "/order");
menuMap.put("个人中心", "/profile");
menuMap.put("设置", "/settings");
等遍历打印的时候,发现输出顺序完全不是你写入的顺序,有时候是"订单"开头,有时候是"个人中心"开头。这不是 bug,这是 HashMap 的特性:数组加链表/红黑树的结构决定了它的遍历顺序取决于 key 的哈希值以及桶的扩容情况,跟插入顺序没有任何关系。
如果这个时候用 LinkedHashMap:
java复制Map<String, String> menuMap = new LinkedHashMap<>();
menuMap.put("首页", "/home");
menuMap.put("订单", "/order");
menuMap.put("个人中心", "/profile");
menuMap.put("设置", "/settings");
只要没有发生插入或者删除操作,遍历结果一定是:
code复制首页 -> /home
订单 -> /order
个人中心 -> /profile
设置 -> /settings
这就是 LinkedHashMap 存在的第一个价值:让 Map 在保持 O(1) 读写效率的同时,还能按插入顺序输出。
1.2 场景二:Set 去重之后还要保住"先后关系"
Set 的场景就更常见了。比如从数据库里查出来一批用户 ID,中间有重复的,你需要去重后按原顺序发给下游服务。HashSet 去重可以,但顺序就听天由命了。关键的是,对这批数据很可能是低频查询或离线导入,本身量不大,但去重后的顺序会被下游用来做拼装、展示,顺序一乱页面就乱了。
LinkedHashSet 就是专门解决这个问题的:
java复制List<String> rawList = Arrays.asList("A1001", "B2002", "A1001", "C3003", "B2002", "D4004");
LinkedHashSet<String> linkedSet = new LinkedHashSet<>(rawList);
System.out.println(linkedSet); // [A1001, B2002, C3003, D4004]
注意结果:重复的元素被剔除,而且第一次出现的相对顺序完整保留。
1.3 场景三:有些场景不只是顺序敏感,还是"最近访问"敏感
还有一类需求没那么直观,但非常典型,比如缓存。我们希望缓存最近访问过的数据,如果缓存满了,就把最久没访问的那个淘汰掉。这不是按插入顺序遍历,而是按访问顺序遍历。LinkedHashMap 的另一个构造函数里有个参数 accessOrder,把它设置为 true,迭代顺序就从"插入顺序"变成"访问顺序"。这是在很多业务模块里实现 LRU 缓存的高频解法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内部实现拆解:LinkedHashMap 如何做到"记住顺序"
2.1 核心设计思路:HashMap 主结构内置双向链表
LinkedHashMap 不是什么从零设计的数据结构,它继承自 HashMap。也就是说,它在 HashMap 的哈希表基础上增加了一条双向链表,用来串联所有已有的 Entry。
这条链表才是顺序的关键。当你执行 put 时,节点会进入哈希桶,同时也会被添加到链表的尾部。遍历的时候,LinkedHashMap 并不像 HashMap 那样去遍历桶数组,而是直接遍历这条链表,所以只要链表维护得好,遍历顺序就完全可控。
这就好比我们去餐厅吃饭。HashMap 的存储像顾客按喜好分散在不同包间,你问服务员"今天所有客人都点了什么",她只能按包间号挨个去问,结果顺序听天由命。LinkedHashMap 的做法是,客人进门时服务员会发一个排队号,等需要统计的时候,只按排队号念一遍即可,又快又稳。
2.2 节点结构:HashMap.Node 上长了前后指针
LinkedHas
