在Java的集合框架中,HashMap是最常用的键值对存储结构,但它内部的元素是无序的,遍历时无法保证和插入顺序一致。如果需要实现按插入顺序保存键值对的需求,LinkedHashMap就是最合适的选择,它在HashMap的基础上做了扩展,完美解决了顺序问题。

LinkedHashMap的基本原理
LinkedHashMap继承自HashMap,它在HashMap的数组+链表+红黑树结构基础上,额外维护了一个双向链表。这个双向链表的作用就是记录所有键值对的插入顺序,每次插入新元素或者访问已有元素时,都会调整这个双向链表的结构,从而保证遍历的时候可以按照插入的先后顺序输出元素。
LinkedHashMap的核心属性有两个:
- head:指向双向链表的头节点,也就是最早插入的元素
- tail:指向双向链表的尾节点,也就是最晚插入的元素
核心构造方法说明
LinkedHashMap提供了多个构造方法,最常用的是无参构造和带初始容量、负载因子的构造方法,默认情况下都是按照插入顺序维护链表:
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapDemo {
public static void main(String[] args) {
// 无参构造,默认初始容量16,负载因子0.75,按插入顺序排序
LinkedHashMap<String, Integer> map1 = new LinkedHashMap<>();
// 自定义初始容量和负载因子的构造方法
LinkedHashMap<String, Integer> map2 = new LinkedHashMap<>(32, 0.8f);
// 第三个参数accessOrder为true时,会按照访问顺序排序,false为插入顺序
LinkedHashMap<String, Integer> map3 = new LinkedHashMap<>(16, 0.75f, false);
}
}
插入顺序的实现逻辑
LinkedHashMap重写了HashMap的newNode方法,在创建新节点的时候,会把节点加入到双向链表的末尾:
// LinkedHashMap的内部节点类,继承自HashMap的Node,增加了before和after指针
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);
}
}
// 重写newNode方法,创建节点后加入双向链表尾部
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
Entry<K,V> p = new Entry<>(hash, key, value, e);
// 把新节点加入双向链表尾部
linkNodeLast(p);
return p;
}
// 维护双向链表的逻辑
private void linkNodeLast(Entry<K,V> p) {
Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
当遍历LinkedHashMap的时候,它不会去遍历HashMap底层的哈希数组,而是直接遍历这个双向链表,从头节点开始依次访问每个节点,自然就保证了顺序和插入顺序一致。
和HashMap的对比
我们可以通过一个简单示例对比两者的顺序差异:
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class CompareDemo {
public static void main(String[] args) {
// 使用HashMap存储
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("a", 1);
hashMap.put("b", 2);
hashMap.put("c", 3);
System.out.println("HashMap遍历结果:");
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 使用LinkedHashMap存储
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("a", 1);
linkedHashMap.put("b", 2);
linkedHashMap.put("c", 3);
System.out.println("LinkedHashMap遍历结果:");
for (Map.Entry<String, Integer> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
运行上述代码可以看到,HashMap的遍历顺序是不固定的,而LinkedHashMap的遍历顺序一定是a、b、c,和插入顺序完全一致。
访问顺序模式说明
LinkedHashMap还有一个特殊的访问顺序模式,当构造方法的accessOrder参数设为true时,每次访问一个元素(包括get和put已存在的键),都会把这个元素移动到双向链表的尾部,这时候遍历顺序就是访问顺序,最近访问的元素会在最后面。这个特性可以用来实现简单的LRU缓存:
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private int maxSize;
public LRUCache(int maxSize) {
// 设置accessOrder为true,开启访问顺序模式
super(16, 0.75f, true);
this.maxSize = maxSize;
}
// 重写removeEldestEntry方法,当元素数量超过最大值时,移除最老的元素(头节点)
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
public static void main(String[] args) {
LRUCache<String, Integer> cache = new LRUCache<>(2);
cache.put("a", 1);
cache.put("b", 2);
// 访问a,a会被移动到尾部
cache.get("a");
// 插入c,此时最老的元素是b,会被移除
cache.put("c", 3);
System.out.println(cache); // 输出 {a=1, c=3}
}
}
使用注意事项
- LinkedHashMap的查询、插入、删除操作的时间复杂度和HashMap基本一致,只是多了维护双向链表的开销,性能略低一点,但顺序特性带来的收益通常更大
- 如果需要保证插入顺序,构造LinkedHashMap时不要把
accessOrder设为true,默认是false,也就是插入顺序模式 - LinkedHashMap是线程不安全的,如果需要多线程环境下使用,需要额外做同步处理,或者使用
Collections.synchronizedMap包装
JavaLinkedHashMap键值对插入顺序修改时间:2026-07-24 14:21:32