linkedhashmap 能记住插入顺序是因为内部维护双向链表,put() 时新节点既写入哈希桶又追加到链表尾;默认构造为插入顺序,accessorder=true 则变为访问顺序;重复 key 的 put 仅更新 value,不改变节点位置;keyset/values/entryset 迭代顺序一致且按插入顺序。

LinkedHashMap 为什么能记住插入顺序
因为 LinkedHashMap 内部维护了一个双向链表,每次调用 put() 时,新节点不仅写入哈希桶,还会被追加到链表尾部。只要不启用访问顺序模式(即构造时没传 true),迭代器就会按这个链表顺序遍历。
注意:这不是“线程安全”的顺序保证——多线程并发 put() 可能导致链表结构错乱,顺序不可预测。单线程下才稳定可靠。
初始化时别误开 accessOrder 模式
默认构造函数 new LinkedHashMap() 是插入顺序;但如果你用了带三个参数的构造函数,第三个布尔值是 accessOrder,设为 true 就会变成 LRU 行为(最近访问的排最后),彻底打乱原始插入顺序。
-
new LinkedHashMap()→ 安全,插入顺序 -
new LinkedHashMap(16, 0.75f, false)→ 等价于默认,显式声明插入顺序 -
new LinkedHashMap(16, 0.75f, true)→ 危险!变成访问顺序,get()都会触发重排序
替换值不会改变节点位置,但重复 key 的 put 会
如果用 put("a", 1) 再次 put("a", 2),虽然 key 相同,但这是更新操作,对应节点在链表中的位置不变。但如果是先 put("a", 1),再 put("b", 2),最后又 put("a", 3),那么 "a" 节点仍保留在原位置(第一次插入处),只是 value 被覆盖。
真正会“移动”节点的,只有 put() 新 key、或 remove() 后再 put() 同 key —— 后者相当于删除+重建,节点会被加到链表尾。
迭代时别混用 keySet / values / entrySet 的顺序差异
三者都遵循同一链表顺序,所以 keySet().iterator()、values().iterator()、entrySet().iterator() 返回的顺序完全一致。但要注意:如果后续对 map 做了结构性修改(比如 clear() 或并发写入),再迭代就不再有顺序保障。
另外,LinkedHashMap 的 forEach() 和增强 for 循环也严格按插入顺序执行,无需额外处理。
容易被忽略的是:子类继承或包装了 LinkedHashMap 后,若重写了 put() 却没调用父类实现,链表就不会更新,顺序立刻失效。










