linkedhashmap 默认按插入顺序遍历,靠双向链表显式维护顺序而非key比较;accessorder=true时get/put移节点至尾部实现lru基础,需重写removeeldestentry控制淘汰。

LinkedHashMap 默认按插入顺序遍历,不是“天然有序”,而是靠链表显式维护
很多人误以为 LinkedHashMap 是“自带排序”的 Map,其实它根本不会比较 key 的大小或自然顺序——它只是在每次 put 时,把新节点追加到双向链表尾部;遍历时顺着链表从头到尾走,所以看起来“有序”。这和 TreeMap 的红黑树排序有本质区别。
- 插入顺序是默认行为,无需额外配置:
new LinkedHashMap()就够了 - 如果用
put重写已有 key(比如map.put("a", 1); map.put("a", 2);),该 key 对应的节点在链表中位置不变,只更新 value - 想让“访问也改变顺序”,必须显式传
true给第三个构造参数:new LinkedHashMap(16, 0.75f, true)
accessOrder = true 是 LRU 缓存的核心开关,但光开开关不够
设了 accessOrder = true 后,get("x") 或 put("x", v) 都会把对应节点移到链表末尾,于是链表头部永远是“最近最少使用”的那个。但这只是基础能力,真正实现 LRU 还得靠重写 removeEldestEntry 方法。
-
removeEldestEntry在每次put后被调用,返回true才会自动删掉链表头节点 - 别忘了它是 protected 方法,子类里要显式 override:
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return size() > MAX_SIZE; } - 注意:这个方法只对
put触发,putAll也会触发;但get不会触发淘汰,只改顺序
和 HashMap 比,性能开销在哪?别在高频遍历场景滥用
LinkedHashMap 插入/查找/删除平均仍是 O(1),但每个节点多存两个指针(before 和 after),内存占用略高;更关键的是——遍历成本和元素个数成正比(O(n)),而 HashMap 的迭代器是散列桶遍历,实际可能跳过大量空桶。
- 如果你的业务只做少量
get/put,但每天要全量 dump 几万条日志进文件,用 LinkedHashMap 会比 HashMap 多出约 10%~15% 的遍历时间 - 并发读写不安全:即使只读,多个线程同时遍历 + 修改,仍可能触发
ConcurrentModificationException - 别用
Collections.synchronizedMap(new LinkedHashMap())做缓存——锁粒度太大,吞吐暴跌;真要线程安全,考虑ConcurrentHashMap+ 外部排序逻辑
初始化参数影响真实行为,负载因子和初始容量不是摆设
LinkedHashMap 构造时传的 initialCapacity 和 loadFactor,不仅影响哈希表扩容时机,还间接决定链表重建频率——因为每次扩容都要重新 hash 并重连所有节点。
立即学习“Java免费学习笔记(深入)”;
- 如果预估要存 1000 个键值对,别写
new LinkedHashMap(16),应设为new LinkedHashMap(1024),避免多次 resize -
loadFactor = 0.75f是平衡空间与冲突的默认值;设太小(如 0.5)会导致频繁扩容;设太大(如 1.0)可能增加链表长度,影响 get 性能 - accessOrder 模式下,频繁访问少数 key 会让链表尾部持续堆积,头部长期“冻住”——此时
removeEldestEntry的判断逻辑必须基于 size,不能依赖时间戳(LinkedHashMap 本身不记录访问时间)










