LinkedHashMap
是 Java 集合框架中的一个重要类,它继承自 HashMap
,并在其基础上 维护了一个双向链表来记录元素的插入顺序或访问顺序。这使得它既能像 HashMap
一样高效,又能保持可预测的迭代顺序。
LinkedHashMap 的特点
特性 | 说明 |
---|---|
键唯一性 | 不允许重复键(基于 equals() 和 hashCode() ) |
有序性 | 默认维护 插入顺序,也可设置为 访问顺序(LRU) |
允许 null | 可以存储 null 键和 null 值 |
非线程安全 | 多线程环境下需要外部同步 |
底层实现 | HashMap + 双向链表(记录顺序) |
性能 | 查询、插入、删除 平均 O(1) |
注意事项
-
线程不安全
-
多线程环境下应使用
Collections.synchronizedMap
包装:Map<String, Integer> syncMap = Collections.synchronizedMap(new LinkedHashMap<>());
-
或使用
ConcurrentHashMap
(但不保证顺序)。
-
-
LRU 缓存实现
-
通过
accessOrder=true
+removeEldestEntry
可轻松实现 LRU 缓存。
-
-
内存占用
-
比
HashMap
占用更多内存(因维护双向链表)。
-
-
迭代性能
-
遍历比
HashMap
更快(直接按链表顺序访问,无需处理哈希桶)。
-
典型应用场景
-
需要保持插入顺序的键值对存储(如配置项加载)。
-
LRU 缓存(最近最少使用算法)。
-
需要按顺序处理的场景(如日志记录、事件队列)。
-
LinkedHashMap
是HashMap
的增强版,通过双向链表维护顺序,适用于需要有序键值对的场景。它既保留了HashMap
的高效性,又提供了可预测的迭代顺序,是 Java 集合框架中非常实用的数据结构。
底层实现
LinkedHashMap
在 HashMap
的基础上扩展
-
双向链表的作用:
-
记录元素的插入顺序(默认)或访问顺序(LRU)。
-
遍历时按链表顺序输出,而非
HashMap
的随机顺序。
-
public class LinkedHashMap<K,V> extends HashMap<K,V> {
// 双向链表的头节点
transient LinkedHashMap.Entry<K,V> head;
// 双向链表的尾节点
transient LinkedHashMap.Entry<K,V> tail;
// 是否按访问顺序排序(true=LRU模式,false=插入顺序)
final boolean accessOrder;
}
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after; // 双向链表的前驱和后继指针
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
核心方法
除了继承 HashMap
的方法外,LinkedHashMap
还扩展了以下功能:
方法 | 说明 |
---|---|
get(Object key) | 获取值,若 accessOrder=true 会将该键移到链表末尾(LRU) |
removeEldestEntry(Map.Entry<K,V> eldest) | 钩子方法,可用于实现 LRU 缓存(返回 true 时删除最旧条目) |
使用示例
示例 1:基本使用(插入顺序)
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("Apple", 1);
map.put("Banana", 2);
map.put("Orange", 3);
// 遍历顺序与插入顺序一致
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 输出顺序:Apple → Banana → Orange
示例 2:LRU 缓存(访问顺序)
// 设置 accessOrder=true,启用 LRU 模式
LinkedHashMap<String, Integer> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
return size() > 3; // 缓存容量为 3,超出时删除最久未访问的条目
}
};
lruCache.put("A", 1);
lruCache.put("B", 2);
lruCache.put("C", 3);
lruCache.get("A"); // 访问 A,将其移到链表末尾
lruCache.put("D", 4); // 超出容量,删除最旧的 B
System.out.println(lruCache); // 输出:{C=3, A=1, D=4}
与 HashMap、TreeMap 对比
特性 | LinkedHashMap | HashMap | TreeMap |
---|---|---|---|
底层实现 | HashMap + 双向链表 | 哈希表 | 红黑树 |
顺序 | 插入顺序 或 访问顺序(LRU) | 无序 | 键的自然排序/自定义排序 |
查询性能 | O(1) | O(1) | O(log n) |
插入/删除性能 | O(1) | O(1) | O(log n) |
允许 null | 允许 null 键和值 | 允许 | 键不能为 null (除非自定义比较器) |
线程安全 | 否 | 否 | 否 |
如何选择?
-
需要 键值对 + 插入顺序/访问顺序 →
LinkedHashMap
-
只需要 键值对,不关心顺序 →
HashMap
-
需要 键值对 + 排序 →
TreeMap
传送门: HashMap详解 HashLink详解 ArrayList详解 CopyOnWriteArrayList详解 HashSet详解