Java LinkedHashMap与TreeMap示例详解
LinkedHashMap基于HashMap与双向链表,保持插入或访问顺序,常用于实现LRU缓存,非线程安全;TreeMap基于红黑树自动排序,查询时间复杂度O(logn),效率稳定但略低于哈希表,键不可为空,适用于排行榜等需要排序的场景。两者均为Map接口的有序实现。
说实话,在学习 Ja va 集合框架时,HashMap 往往是大家最先接触的 Map 实现。但在实际开发里,另外两个“狠角色”——LinkedHashMap 和 TreeMap——同样出场率极高。它们都实现了 Map 接口,但脾性截然不同。
这篇文章会从概念、底层原理、核心差异、适用场景和代码实例这几个维度,帮你把它们彻底吃透。
一、Map 集合基础回顾
Map 就是典型的“键值对(key-value)”数据结构,形如 key → value。例如:"张三" → 18,"李四" → 20。
Ja va 中常见的 Map 实现大致分三类:
| 类型 | 特点 |
|---|---|
| HashMap | 无序 |
| LinkedHashMap | 有序 |
| TreeMap | 自动排序 |
二、LinkedHashMap
1. 什么是 LinkedHashMap
LinkedHashMap 是 HashMap 的子类,它的最大特色就在于——能够完美保持元素的插入顺序。
举个例子:往 Map 里依次放入 1 → A、3 → C、2 → B,遍历时拿到的顺序依然是 1、3、2,绝不会像 HashMap 那样打乱顺序。
2. LinkedHashMap 底层原理
一句话概括:LinkedHashMap = HashMap + 双向链表。
它内部同时保持了 HashMap 的哈希存储结构,以及一个双向链表来维护元素的顺序。整体结构可以理解为:数组 + 链表 + 红黑树 + 双向链表。HashMap 负责高效的查询,双向链表则负责“记住”元素进来的先后顺序。
3. LinkedHashMap 的特点
| 特点 | 说明 |
|---|---|
| 有序 | 按插入顺序排列 |
| 查询快 | 基于 HashMap |
| 允许 null | key 和 value 都允许 |
| 非线程安全 | 多线程需额外处理 |
4. LinkedHashMap 基本使用
示例:保持插入顺序
import ja va.util.LinkedHashMap;
public class Demo {
public static void main(String[] args) {
LinkedHashMap map = new LinkedHashMap<>();
map.put(3, "Ja va");
map.put(1, "Python");
map.put(2, "C++");
System.out.println(map);
}
}
输出:
{3=Ja va, 1=Python, 2=C++}
可以看到,顺序与插入顺序完全一致。
5. 遍历 LinkedHashMap
import ja va.util.LinkedHashMap;
import ja va.util.Map;
public class Demo {
public static void main(String[] args) {
LinkedHashMap map = new LinkedHashMap<>();
map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
for (Map.Entry entry : map.entrySet()) {
System.out.println(
entry.getKey() + " : " + entry.getValue()
);
}
}
}
输出:
1 : 张三
2 : 李四
3 : 王五
6. LinkedHashMap 的访问顺序
LinkedHashMap 支持两种顺序模式:
| 顺序 | 说明 |
|---|---|
| 插入顺序 | 默认 |
| 访问顺序 | 最近访问的排后面 |
开启访问顺序模式也很简单:
new LinkedHashMap<>(16, 0.75f, true)
第三个参数传 true 就表示开启访问顺序。
示例:LRU缓存思想
import ja va.util.LinkedHashMap;
import ja va.util.Map;
public class Demo {
public static void main(String[] args) {
LinkedHashMap map =
new LinkedHashMap<>(16, 0.75f, true);
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
//访问元素
map.get(1);
System.out.println(map);
}
}
输出:
{2=B, 3=C, 1=A}
因为 1 被访问后移动到了末尾,这正是很多缓存系统的核心策略——LRU(最近最少使用)。
三、TreeMap
1. 什么是 TreeMap
TreeMap 最大的特点:自动排序。无论你插入的顺序如何,它都会按照 key 来自动排序。
2. TreeMap 底层原理
TreeMap 底层基于红黑树(Red-Black Tree)实现。红黑树是一种自平衡的二叉搜索树,核心特性很突出:查询效率高、自动排序、增删改效率稳定。
时间复杂度方面:
| 操作 | 时间复杂度 |
|---|---|
| put | O(log n) |
| get | O(log n) |
| remove | O(log n) |
3. TreeMap 的特点
| 特点 | 说明 |
|---|---|
| 自动排序 | 按 key 排序 |
| 不允许 key 为 null | 会报空指针异常 |
| 查询效率稳定 | 红黑树实现 |
| 非线程安全 | 多线程需同步 |
四、TreeMap 默认排序
默认情况下,TreeMap 会按照 key 的自然顺序进行排序。
示例:数字排序
import ja va.util.TreeMap;
public class Demo {
public static void main(String[] args) {
TreeMap map = new TreeMap<>();
map.put(3, "Ja va");
map.put(1, "Python");
map.put(2, "C++");
System.out.println(map);
}
}
输出:
{1=Python, 2=C++, 3=Ja va}
即使插入顺序是 3、1、2,输出结果仍然是自动排序后的结果。
五、TreeMap 字符串排序
import ja va.util.TreeMap;
public class Demo {
public static void main(String[] args) {
TreeMap map = new TreeMap<>();
map.put("banana", 1);
map.put("apple", 2);
map.put("cat", 3);
System.out.println(map);
}
}
输出:
{apple=2, banana=1, cat=3}
字符串会按字母顺序进行排序。
六、TreeMap 自定义排序
如果想让 TreeMap 按自己的规则排序,可以使用Comparator 比较器来实现。
示例:降序排序
import ja va.util.Comparator;
import ja va.util.TreeMap;
public class Demo {
public static void main(String[] args) {
TreeMap map =
new TreeMap<>(Comparator.reverseOrder());
map.put(1, "A");
map.put(3, "C");
map.put(2, "B");
System.out.println(map);
}
}
输出:
{3=C, 2=B, 1=A}
七、TreeMap 自定义对象排序
如果 key 是自定义对象,那么该对象必须实现 Comparable 接口,或者在构造 TreeMap 时传入一个 Comparator,否则运行时就会报错。
示例:学生年龄排序
Student 类
class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "-" + age;
}
}
使用 Comparator
import ja va.util.Comparator;
import ja va.util.TreeMap;
public class Demo {
public static void main(String[] args) {
TreeMap map =
new TreeMap<>((o1, o2) -> o1.age - o2.age);
map.put(new Student("张三", 18), "Ja va");
map.put(new Student("李四", 20), "Python");
map.put(new Student("王五", 19), "C++");
System.out.println(map);
}
}
输出:
{张三-18=Ja va, 王五-19=C++, 李四-20=Python}
八、LinkedHashMap 与 TreeMap 对比
| 对比项 | LinkedHashMap | TreeMap |
|---|---|---|
| 是否有序 | 按插入顺序 | 自动排序 |
| 底层结构 | Hash表 + 双向链表 | 红黑树 |
| 查询效率 | O(1) | O(log n) |
| 是否允许 null key | 允许 | 不允许 |
| 使用场景 | 记录顺序 | 排序需求 |
九、如何选择?
使用 LinkedHashMap
- 需要保持插入顺序
- 最近访问记录
- LRU缓存
- 浏览历史
例如:最近播放歌曲、最近浏览商品等场景。
使用 TreeMap
- 自动排序
- 排行榜
- 成绩排序
- 字典排序
例如:学生成绩排名、商品价格排序等场景。
十、小tips
1. LinkedHashMap 和 HashMap 区别?
LinkedHashMap 是有序的,内部多了一个双向链表;而 HashMap 是无序的,纯哈希结构。
2. TreeMap 为什么能排序?
因为底层是红黑树,插入元素时会自动比较 key,从而实现排序。
3. TreeMap 为什么不能为 null?
因为排序时需要调用 compareTo() 方法来比较 key,而 null 无法被比较,会直接抛出空指针异常。
4. LinkedHashMap 为什么适合做缓存?
因为它支持访问顺序这种模式:最近访问的数据会自动移动到链表尾部。这正好符合 LRU(最近最少使用)缓存淘汰策略。
十一、总结
LinkedHashMap
核心关键词:有序、插入顺序、双向链表、缓存。适合那种既想要 HashMap 的高效查询,又需要保持元素顺序的场景。
TreeMap
核心关键词:自动排序、红黑树、比较器、有序Map。适合那些需要按 key 自动排序的场景。
十二、用一张图来表示三种map
Map
├── HashMap
│ ├── 无序
│ └── 查询快
│
├── LinkedHashMap
│ ├── 有序
│ ├── 双向链表
│ └── 适合缓存
│
└── TreeMap
├── 自动排序
├── 红黑树
└── 适合排行榜
真正掌握集合框架后,其实我们会发现:Ja va 集合本质就是“数据结构 + 算法思想”的具体实现。


































