TreeMap:红黑树上的排序 Map
TreeMap 是什么
TreeMap 是基于红黑树的 Map 实现,核心能力是:按键排序存储,按 key 快速查找。
java
// HashMap:key 无序
Map<String, Integer> hash = new HashMap<>();
hash.put("z", 1); hash.put("a", 2); hash.put("m", 3);
System.out.println(hash); // 无序,如 {z=1, a=2, m=3}
// TreeMap:key 有序(字典序)
Map<String, Integer> tree = new TreeMap<>();
tree.put("z", 1); tree.put("a", 2); tree.put("m", 3);
System.out.println(tree); // {a=2, m=3, z=1}红黑树:TreeMap 的底座
为什么不用普通 BST
普通二叉搜索树(BST)在极端情况下会退化成链表:
已排序数据插入 BST:
1
\
2
\
3
\
4 ← 退化成链表,查找 O(n)
红黑树(自动平衡):
2(黑)
/ \
1(红) 3(红) ← 自动旋转,查找 O(log n)红黑树的五大规则
- 节点非红即黑
- 根节点是黑色
- 红节点的子节点必须是黑色(不能连续两个红节点)
- 每条路径的黑色节点数相同
- 叶子节点(NIL)都是黑色
这五条规则保证了:树高 ≤ 2 × log₂(n),即查找始终是 O(log n)。
TreeMap 的独有能力
导航操作:TreeMap 最有价值的功能
java
NavigableMap<Integer, String> map = new TreeMap<>();
map.put(10, "十");
map.put(30, "三十");
map.put(20, "二十");
map.put(50, "五十");
map.put(40, "四十");
// 最值
map.firstKey(); // 10
map.lastKey(); // 50
// 邻居查询
map.lowerKey(30); // 20(小于 30 的最大)
map.higherKey(30); // 40(大于 30 的最小)
map.floorKey(35); // 30(≤ 35 的最大)
map.ceilingKey(35);// 40(≥ 35 的最小)
// 范围视图
map.subMap(20, 50); // [20, 30, 40] 20 ≤ key < 50
map.headMap(30); // [10, 20] key < 30
map.tailMap(40); // [40, 50] key ≥ 40HashMap 完全没有这些能力。
反向遍历
java
NavigableMap<Integer, String> desc = map.descendingMap();
// {50=五十, 40=四十, 30=三十, 20=二十, 10=十}两种排序方式
自然排序:元素实现 Comparable
java
// String 按字典序,Integer 按数值大小
TreeMap<String, Integer> natural = new TreeMap<>();
natural.put("cherry", 3);
natural.put("apple", 1);
natural.put("banana", 2);
System.out.println(natural); // {apple=1, banana=2, cherry=3}自定义排序:传入 Comparator
java
// 按字符串长度排序
TreeMap<String, Integer> byLen = new TreeMap<>(
Comparator.comparingInt(String::length)
);
byLen.put("hi", 1);
byLen.put("banana", 2);
byLen.put("apple", 3);
System.out.println(byLen); // {hi=1, apple=3, banana=2}
// 降序排列
TreeMap<Integer, String> desc = new TreeMap<>(Comparator.reverseOrder());
desc.put(1, "one"); desc.put(3, "three"); desc.put(2, "two");
System.out.println(desc); // {3=three, 2=two, 1=one}两者谁优先
Comparator 优先。如果创建 TreeMap 时传了 Comparator,元素的 Comparable 实现就被忽略:
java
class User implements Comparable<User> {
String name;
int score;
@Override
public int compareTo(User other) {
return this.score - other.score; // 按分数升序
}
}
// ❌ Comparator 优先,compareTo 被忽略
TreeMap<User, String> map = new TreeMap<>(
Comparator.comparing(u -> u.name) // 按姓名排序
);null 的处理
key 不能为 null
java
TreeMap<String, Integer> map = new TreeMap<>();
map.put(null, 1); // NullPointerException!因为红黑树需要比较 key 来决定位置,null 无法参与比较。
value 可以为 null
java
TreeMap<String, Integer> map = new TreeMap<>();
map.put("a", null); // ✅ OK
map.put("b", null); // ✅ OK如果非要 key 为 null
java
// 使用接受 null 的 Comparator
TreeMap<String, Integer> nullable = new TreeMap<>(
Comparator.nullsLast(Comparator.naturalOrder())
);
nullable.put(null, 1); // ✅ null 排在最后典型应用场景
场景 1:排行榜
java
TreeMap<Integer, String> leaderboard = new TreeMap<>(Comparator.reverseOrder());
leaderboard.put(1500, "张三");
leaderboard.put(2000, "李四");
leaderboard.put(1800, "王五");
// 获取前 3 名
leaderboard.descendingMap().entrySet().stream()
.limit(3)
.forEach(e -> System.out.println(e.getValue() + ": " + e.getKey()));
// 输出:李四: 2000, 王五: 1800, 张三: 1500场景 2:时间区间查找
java
TreeMap<LocalTime, String> schedule = new TreeMap<>();
schedule.put(LocalTime.of(9, 0), "会议");
schedule.put(LocalTime.of(14, 0), "演示");
schedule.put(LocalTime.of(18, 0), "下班");
// 当前时间是 10:00,找最近的下一个事件
LocalTime now = LocalTime.of(10, 0);
Map.Entry<LocalTime, String> next = schedule.higherEntry(now);
System.out.println("接下来: " + next.getValue()); // 演示场景 3:分层统计
java
// 按年龄段分组
TreeMap<Integer, List<String>> byAge = new TreeMap<>();
List.of("张三(25)", "李四(30)", "王五(25)", "赵六(35)")
.forEach(name -> {
int age = Integer.parseInt(name.replaceAll("\\D+", ""));
byAge.computeIfAbsent(age, k -> new ArrayList<>()).add(name);
});
System.out.println(byAge);
// {25=[张三(25), 王五(25)], 30=[李四(30)], 35=[赵六(35)]}性能特点
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| put | O(log n) | 插入 + 可能触发再平衡 |
| get | O(log n) | 二叉搜索 |
| remove | O(log n) | 删除 + 可能触发再平衡 |
| first/last | O(log n) | 最值查询 |
| lower/higher | O(log n) | 邻居查询 |
| subMap | O(log n + k) | k = 返回元素数 |
TreeMap vs HashMap
| 维度 | TreeMap | HashMap |
|---|---|---|
| 排序 | ✅ 按 key 排序 | ❌ 无序 |
| 查找速度 | O(log n) | O(1) 均摊 |
| 导航操作 | ✅ 丰富 | ❌ 不支持 |
| key 为 null | ❌ 不允许 | ✅ 允许一个 |
| 内存占用 | 较高(树节点) | 较低 |
| 适用场景 | 需要排序/导航 | 纯粹映射 |
总结
| 要点 | 说明 |
|---|---|
| 底层结构 | 红黑树(自平衡 BST) |
| 核心能力 | 按 key 排序 + 导航操作 |
| 时间复杂度 | O(log n),稳定 |
| key 要求 | 必须可比较(或提供 Comparator) |
| 独有功能 | first/last/lower/higher/subMap |
| 线程安全 | 非安全,多线程用 ConcurrentSkipListMap |
一句话:HashMap 管「找不找得到」,TreeMap 管「排第几」。
