在Java集合体系中,LinkedHashMap是HashMap的一个子类,它最大的特点就是能够保持元素的顺序,而普通的HashMap在遍历时元素的顺序是不固定的。这种有序特性并不是通过额外的排序逻辑实现的,而是基于底层的数据结构扩展。

LinkedHashMap的底层结构
LinkedHashMap继承自HashMap,因此它的底层存储依然是基于数组+链表+红黑树的结构,和HashMap完全一致。除此之外,LinkedHashMap额外维护了一个双向链表,这个链表的作用就是记录所有元素的顺序。我们可以看一下LinkedHashMap中定义的几个关键字段:
// 双向链表的头节点,指向顺序最靠前的元素 transient LinkedHashMap_Entry<K,V> head; // 双向链表的尾节点,指向顺序最靠后的元素 transient LinkedHashMap_Entry<K,V> tail; // 访问顺序开关,true表示按访问顺序排序,false表示按插入顺序排序,默认是false final boolean accessOrder;
其中LinkedHashMap_Entry是LinkedHashMap的内部类,它继承自HashMap的Node类,额外增加了before和after两个指针,用来维护双向链表的前后关系:
static class LinkedHashMap_Entry<K,V> extends HashMap_Node<K,V> {
LinkedHashMap_Entry<K,V> before, after;
LinkedHashMap_Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
插入顺序的实现逻辑
当accessOrder为false时,LinkedHashMap按照元素的插入顺序维护双向链表。在插入新元素的时候,LinkedHashMap会重写HashMap的newNode方法,在创建节点之后,将新节点加入到双向链表的尾部:
// 重写HashMap的newNode方法,创建LinkedHashMap_Entry节点
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap_Entry<K,V> p =
new LinkedHashMap_Entry<K,V>(hash, key, value, e);
// 将新节点加入双向链表尾部
linkNodeLast(p);
return p;
}
// 将节点链接到双向链表尾部
private void linkNodeLast(LinkedHashMap_Entry<K,V> p) {
LinkedHashMap_Entry<K,V> last = tail;
tail = p;
// 如果链表为空,头节点也指向当前节点
if (last == null)
head = p;
else {
// 否则将当前节点链接到原尾节点之后
p.before = last;
last.after = p;
}
}
当遍历LinkedHashMap的时候,它不会去遍历底层的HashMap数组,而是直接遍历这个双向链表,从头节点开始依次访问after指针指向的节点,因此遍历的顺序就是元素插入的顺序。
访问顺序的实现逻辑
当accessOrder设置为true时,LinkedHashMap会按照元素的访问顺序维护双向链表,最近被访问的元素会被移动到双向链表的尾部。这个逻辑是通过重写HashMap的afterNodeAccess方法实现的,这个方法会在调用get方法访问元素之后被触发:
// 重写get方法,当accessOrder为true时,访问后调整节点顺序
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
// 如果开启访问顺序,访问后调整节点位置
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
// 访问节点后将节点移动到双向链表尾部
void afterNodeAccess(Node<K,V> e) {
LinkedHashMap_Entry<K,V> last;
// 如果当前节点不是尾节点,才需要调整
if (accessOrder && (last = tail) != e) {
LinkedHashMap_Entry<K,V> p =
(LinkedHashMap_Entry<K,V>)e, b = p.before, a = p.after;
// 先断开当前节点的after指针
p.after = null;
// 如果当前节点是头节点,将头节点指向它的下一个节点
if (b == null)
head = a;
else
b.after = a;
// 如果当前节点不是尾节点,将它的下一个节点的before指向它的上一个节点
if (a != null)
a.before = b;
else
last = b;
// 将当前节点链接到双向链表尾部
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
}
}
这种访问顺序的特性可以用来实现简单的LRU缓存,只需要继承LinkedHashMap,重写removeEldestEntry方法,当元素数量超过阈值时移除最久未访问的元素即可。
两种顺序模式的对比
我们可以通过一个表格来对比两种顺序模式的区别:
| 顺序模式 | accessOrder值 | 顺序规则 | 触发顺序调整的操作 |
|---|---|---|---|
| 插入顺序 | false(默认) | 元素按照插入的先后顺序排列,先插入的在前,后插入的在后 | 插入新元素时调整 |
| 访问顺序 | true | 最近被访问的元素排在最后,最久未访问的元素排在最前 | 插入新元素、调用get方法访问元素时调整 |
使用注意事项
- LinkedHashMap因为额外维护了双向链表,所以内存占用会比普通的HashMap更高,在不需要顺序特性的场景下不建议使用。
- 插入顺序模式下,修改已存在元素的value值不会改变元素的顺序,只有插入新元素和访问操作(开启访问顺序时)才会调整顺序。
- LinkedHashMap不是线程安全的,多线程环境下如果需要使用,需要额外做同步处理,或者使用Collections.synchronizedMap包装。
需要注意的是,LinkedHashMap的有序特性是基于双向链表实现的,和TreeMap的排序逻辑完全不同,TreeMap是通过比较器或者键的自然顺序来排序的,而LinkedHashMap只是记录插入或者访问的先后顺序,不会对键进行排序。
LinkedHashMapJava有序特性HashMap双向链表修改时间:2026-07-16 16:39:35