在Java的集合框架中,Map接口的实现类里,HashMap是最常用的,但它存在一个明显的特点:迭代顺序是不确定的,和插入顺序没有关联。如果我们需要保持键值对的插入顺序或者访问顺序,LinkedHashMap就是最合适的选择。

LinkedHashMap保持顺序的核心原理
LinkedHashMap是HashMap的子类,它在HashMap的基础上,额外维护了一个双向链表。这个双向链表会记录所有键值对的顺序,每个节点除了存储HashMap需要的哈希值、键、值、下一个节点指针之外,还会存储前驱和后继节点的指针,这样就可以通过双向链表来维护元素的顺序。
LinkedHashMap有两个核心的构造参数,用来控制顺序的类型:
- accessOrder:布尔类型,默认是false,表示按照插入顺序维护链表;如果设置为true,就会按照访问顺序(最近访问的元素会被移到链表末尾)维护链表。
- 默认情况下,LinkedHashMap的迭代顺序就是元素的插入顺序,这也是它和普通HashMap最大的区别之一。
基础用法:保持插入顺序
最基础的用法就是创建LinkedHashMap对象,直接插入元素,迭代的时候就会按照插入的顺序输出,下面是具体的代码示例:
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapDemo {
public static void main(String[] args) {
// 创建LinkedHashMap,默认accessOrder为false,保持插入顺序
Map<String, Integer> scoreMap = new LinkedHashMap<>();
// 插入元素
scoreMap.put("语文", 90);
scoreMap.put("数学", 95);
scoreMap.put("英语", 88);
scoreMap.put("物理", 92);
// 迭代输出,顺序和插入顺序一致
System.out.println("按插入顺序迭代:");
for (Map.Entry<String, Integer> entry : scoreMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}运行上面的代码,输出的顺序就是语文、数学、英语、物理,和插入的顺序完全一致,而如果使用HashMap的话,输出的顺序是不确定的。
进阶用法:保持访问顺序
如果我们希望最近访问的元素排在最后,也就是按照访问顺序维护映射,可以在构造LinkedHashMap的时候把accessOrder设置为true,下面是示例代码:
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapAccessOrderDemo {
public static void main(String[] args) {
// 创建LinkedHashMap,设置accessOrder为true,保持访问顺序
Map<String, Integer> cacheMap = new LinkedHashMap<>(16, 0.75f, true);
// 插入初始元素
cacheMap.put("key1", 100);
cacheMap.put("key2", 200);
cacheMap.put("key3", 300);
System.out.println("初始顺序:");
for (String key : cacheMap.keySet()) {
System.out.print(key + " ");
}
System.out.println();
// 访问key1,此时key1会被移到链表末尾
cacheMap.get("key1");
System.out.println("访问key1后的顺序:");
for (String key : cacheMap.keySet()) {
System.out.print(key + " ");
}
System.out.println();
}
}运行上面的代码,初始顺序是key1、key2、key3,访问key1之后,顺序会变成key2、key3、key1,说明key1被移到了最后,符合访问顺序的规则。
LinkedHashMap和HashMap的核心差异对比
为了更清晰地了解LinkedHashMap的特点,我们把它的和HashMap做一个核心差异对比:
| 对比项 | HashMap | LinkedHashMap |
|---|---|---|
| 迭代顺序 | 不确定,和插入顺序无关 | 可配置,默认插入顺序,可设置为访问顺序 |
| 底层结构 | 数组+链表+红黑树 | 数组+链表+红黑树+双向链表 |
| 性能 | 基本操作(增删改查)性能和LinkedHashMap接近,迭代速度更快 | 因为要维护双向链表,增删操作会稍微慢一点,迭代速度比HashMap慢 |
| 适用场景 | 不需要保证顺序的场景 | 需要保证插入顺序或者访问顺序的场景,比如LRU缓存实现 |
使用注意事项
在使用LinkedHashMap的时候,有几个点需要注意:
- LinkedHashMap不是线程安全的,如果有多个线程同时操作,并且至少有一个线程修改了映射的结构,那么必须在外部做同步处理,或者使用
Collections.synchronizedMap来包装。 - 当accessOrder设置为true的时候,所有的访问操作(包括get、put已经存在的键)都会改变元素的顺序,而插入新的键只会把新键加到链表末尾。
- 因为LinkedHashMap维护了额外的双向链表,所以它的内存占用会比HashMap更高,如果对内存比较敏感,并且不需要保证顺序,优先选择HashMap。
常见应用场景
LinkedHashMap最常见的应用场景就是实现LRU(最近最少使用)缓存,因为我们可以重写它的removeEldestEntry方法,当缓存大小超过阈值的时候,自动删除最久没有访问的元素,下面是简单的LRU缓存实现示例:
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public LRUCache(int maxSize) {
// 设置accessOrder为true,按照访问顺序维护
super(16, 0.75f, true);
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
// 当大小超过maxSize的时候,删除最老的元素
return size() > maxSize;
}
public static void main(String[] args) {
LRUCache<String, Integer> cache = new LRUCache<>(3);
cache.put("a", 1);
cache.put("b", 2);
cache.put("c", 3);
System.out.println("初始缓存:" + cache);
cache.get("a"); // 访问a,a移到末尾
cache.put("d", 4); // 插入d,超过大小,删除最老的b
System.out.println("操作后缓存:" + cache);
}
}运行上面的代码,初始缓存是{a=1, b=2, c=3},访问a之后插入d,最老的b会被删除,最终缓存是{c=3, a=1, d=4},符合LRU的规则。
LinkedHashMapJava映射顺序HashMapMap接口迭代顺序修改时间:2026-05-25 21:44:12