LinkedHashMap 默认按插入顺序维护元素,因其内部维护双向链表,节点通过 before/after 指针串联;启用访问顺序需构造时传 true,get/put 会将节点移至尾部,配合重写 removeEldestEntry 实现 LRU 淘汰。

LinkedHashMap 默认按插入顺序维护元素,这是它和 HashMap 最本质的区别;如果需要访问顺序(LRU),得显式启用。
为什么 LinkedHashMap 能记住顺序?
因为它的内部同时维护了一个双向链表,每个 Node 不仅参与哈希桶的链表/红黑树结构,还通过 before 和 after 指针串成一条独立的访问链。插入新节点时,会自动追加到链表尾部;遍历时,entrySet().iterator() 就是按这条链走的。
注意:这个行为只对 put(K, V)、putAll(Map) 等插入操作生效;get(K) 默认不改变顺序 —— 除非你用的是访问顺序模式。
如何启用访问顺序(LRU 缓存)?
构造时传入第三个参数 true:
立即学习“Java免费学习笔记(深入)”;
MaplruCache = new LinkedHashMap<>(16, 0.75f, true);
启用后:get(K) 和 put(K, V) 都会把对应节点移到链表尾部;迭代时最久未使用的在头部,适合手动淘汰(比如重写 removeEldestEntry)。
- 必须重写
removeEldestEntry(Map.Entry)才能触发自动删除,否则只是“记序”不“淘汰” - 该方法在每次
put后被调用,返回true才移除头节点 - 不要在该方法里调用
get或put,会引发并发修改异常
遍历顺序和迭代器行为
keySet()、values()、entrySet() 的迭代器都严格遵循内部链表顺序,且是 fail-fast 的。
常见误判点:
-
toArray()返回的数组也保持顺序,但它是副本,后续修改 map 不影响数组 -
replaceAll(BiFunction)不改变节点位置,只更新 value;而computeIfAbsent这类方法在插入新 key 时仍会追加到尾部 - 如果用
put更新已有 key 的 value,插入顺序不变;但在访问顺序模式下,这次put会让该 entry 移到尾部
和 TreeMap 的关键区别在哪?
TreeMap 是按 key 的自然序或自定义 Comparator 排序,和插入无关;LinkedHashMap 的顺序完全由操作时序决定,不依赖 key 值本身。
性能上:
-
LinkedHashMap的get/put平均仍是 O(1),比TreeMap的 O(log n) 快 - 内存开销略高:每个节点多两个指针(
before/after) - 序列化时,
LinkedHashMap会把链表结构一并写入,反序列化后顺序不变;TreeMap只保证逻辑序,不保证重建过程中的插入路径
真正容易被忽略的是:即使你没显式用到顺序特性,只要用了 LinkedHashMap,就默认承担了额外的链表维护成本;如果业务从不遍历、也不关心顺序,用 HashMap 更轻量。










