💡 摘要:你是否遇到过这样的需求——希望一个 Map 中的键值对能自动按“键”的大小排序? 比如,实现一个按价格排序的商品目录、按时间排序的日志记录,或者需要快速找到“最接近的键”(如楼层查找、版本匹配)? 此时,
HashMap无法满足,LinkedHashMap只能按插入顺序排序。 而TreeMap就是为此而生:它是一个基于红黑树的有序映射,支持按键的自然顺序或自定义比较器排序,并提供高效的范围查询、最值查找等操作。 本文将带你深入理解TreeMap的设计思想、底层红黑树原理、核心 API 使用,并对比HashMap与TreeMap的适用场景,最后附上面试高频问题解析,助你彻底掌握这一“有序王者”。
TreeMap 是 Java 集合框架中 SortedMap 和 NavigableMap 接口的核心实现。
它最大的特点是:键值对按照键的顺序存储和遍历。
TreeMap<Integer, String> map = new TreeMap<>();
map.put(3, "three");
map.put(1, "one");
map.put(2, "two");
System.out.println(map); // 输出 {1=one, 2=two, 3=three}这与 HashMap 的无序性形成鲜明对比。
TreeMap是一个基于红黑树(Red-Black Tree) 的有序Map,支持按键的自然顺序或自定义比较器排序,提供O(log n)的插入、删除、查找性能。
// 按学生成绩排名
TreeMap<Integer, String> rank = new TreeMap<>(Comparator.reverseOrder());
rank.put(95, "Alice");
rank.put(87, "Bob");
rank.put(92, "Charlie");// 查询价格在 [100, 500] 之间的商品
SortedMap<Double, Product> sub = map.subMap(100.0, 500.0);// 找到小于等于 49 的最大键(地板键)
map.floorKey(49); // 如 45
// 找到大于等于 51 的最小键(天花板键)
map.ceilingKey(51); // 如 55虽然 PriorityQueue 更适合优先队列,但 TreeMap 提供了更丰富的有序操作。
特性 | HashMap | TreeMap |
|---|---|---|
数据结构 | 数组 + 链表/红黑树 | 红黑树(自平衡二叉搜索树) |
排序 | 无序 | 按键有序(自然顺序或 Comparator) |
时间复杂度 | 平均 O(1) | O(log n) |
是否允许 null 键 | ✅(一个) | ❌(自然排序时)✅(自定义 Comparator 支持) |
线程安全 | ❌ | ❌ |
适用场景 | 通用、高性能查找 | 有序遍历、范围查询、最值查找 |
✅ 总结:
HashMapTreeMap这些规则确保了树的“近似平衡”,最坏情况下高度不超过
2log(n+1)。
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;
// ...
}🔍 这些操作在
TreeMap源码中由fixAfterInsertion和fixAfterDeletion方法实现,逻辑复杂但保证了O(log n)的性能。
TreeMap<String, Integer> map = new TreeMap<>();
map.put("banana", 2);
map.put("apple", 1);
map.put("cherry", 3);
System.out.println(map); // {apple=1, banana=2, cherry=3}// 按字符串长度排序
TreeMap<String, Integer> map = new TreeMap<>(
Comparator.comparing(String::length)
);
map.put("hi", 1);
map.put("hello", 2);
map.put("a", 3);
// 输出 {a=3, hi=1, hello=2}SortedMap<String, Integer> sub = map.subMap("b", "d");
// 返回键在 [b, d) 范围内的视图map.firstKey(); // 最小键
map.lastKey(); // 最大键
map.firstEntry(); // 最小键值对
map.lastEntry(); // 最大键值对map.lowerKey("c"); // 小于 c 的最大键
map.floorKey("c"); // 小于等于 c 的最大键
map.ceilingKey("c"); // 大于等于 c 的最小键
map.higherKey("c"); // 大于 c 的最小键📌 这些方法在实现“模糊匹配”、“版本路由”、“楼层查找”等场景非常有用。
TreeMap 不是线程安全的。多线程并发访问需外部同步:
TreeMap<Object, Object> map = Collections.synchronizedSortedMap(
new TreeMap<>()
);或者使用 ConcurrentSkipListMap 作为线程安全的有序 Map 替代方案。
场景 | 推荐使用 |
|---|---|
通用、高性能查找 | HashMap |
需要有序遍历 | TreeMap |
范围查询、最值查找 | TreeMap |
高并发有序映射 | ConcurrentSkipListMap |
插入顺序遍历 | LinkedHashMap |
⚠️ 注意:
TreeMap的O(log n)性能在数据量小时与HashMap差距不大,但数据量大时明显慢于HashMap的O(1)。
自然排序时 key 不能为 null:Comparable 接口不支持 null 比较
自定义 Comparator 可支持 null:
new TreeMap<>((a, b) -> {
if (a == null) return b == null ? 0 : -1;
if (b == null) return 1;
return a.compareTo(b);
});性能敏感场景慎用:O(log n) 比 O(1) 慢
迭代器是 fail-fast 的:并发修改会抛 ConcurrentModificationException
Comparator 而非 Comparable,更灵活答:
TreeMap基于红黑树(Red-Black Tree) 实现,是一种自平衡二叉搜索树,保证插入、删除、查找的时间复杂度为O(log n)。
答:
HashMap 基于哈希表,无序,平均 O(1) 性能;TreeMap 基于红黑树,按键有序,O(log n) 性能;HashMap 允许一个 null 键,TreeMap 自然排序时不支持 null 键;TreeMap 支持范围查询、最值查找等有序操作。答: 通过红黑树的结构:左子树 < 根 < 右子树。 中序遍历即可得到有序序列。 插入/删除时通过旋转和变色保持平衡和有序。
答:
Comparable),则不支持 null 键,否则抛 NullPointerException;答: 红黑树是一种自平衡二叉搜索树,通过五条规则(如红节点子节点为黑、路径黑节点数相同等)确保树的高度近似平衡,最坏情况下查找、插入、删除均为
O(log n)。
答:
subMap返回的是原TreeMap的一个视图(View),不是副本。 对视图的修改会反映到原 Map 上,反之亦然。 它支持动态范围查询,非常高效。
答:
TreeMap本身不是线程安全的。 可以使用Collections.synchronizedSortedMap(new TreeMap<>())包装, 或使用ConcurrentSkipListMap,它是TreeMap的并发替代品,基于跳表实现,支持高并发有序操作。
TreeMap 是 Java 集合中“有序性”的代表。
它牺牲了 HashMap 的极致性能,换来了按键排序、范围查询、邻近查找等强大能力。
理解 TreeMap,不仅是掌握一个集合类,更是理解了 红黑树、自平衡、有序数据结构的设计智慧。
在需要“顺序”的场景中,
TreeMap是你最可靠的伙伴。