很多 Java 开发者知道:TreeMap 和 TreeSet 能实现自动排序,但背后的机制究竟是什么?红黑树是如何支撑这一能力的?今天带你从底层结构、核心逻辑、源码实现全面解构。
一、为何 TreeMap / TreeSet 能自动排序?
原因在于它们底层采用了 红黑树(Red-Black Tree),一种具有自平衡性质的二叉搜索树。每次插入或删除元素时,红黑树会自动旋转或染色,以维持排序和查找性能。
-
TreeMap<K, V> 按 Key 排序;
-
TreeSet<E> 基于 TreeMap<E, Object> 实现;
-
插入元素时会自动维护顺序;
-
支持自定义排序器:Comparator。
二、红黑树简述:五大性质
红黑树是一种特殊的二叉查找树,额外维护以下五条性质以保持近似平衡:
-
每个节点要么是红色要么是黑色;
-
根节点必须是黑色;
-
所有叶子节点(NIL)是黑色;
-
红色节点的子节点只能是黑色(不能连续出现两个红色节点);
-
任意节点到其所有后代叶子节点的路径上,黑色节点数量相同。
这使得树的最长路径不会超过最短路径的两倍,从而保证了查找操作的时间复杂度为 O(log n)。
三、TreeMap 插入排序的核心逻辑
当你执行如下代码时:
TreeMap<Integer, String> map = new TreeMap<>();
map.put(3, "C");
map.put(1, "A");
map.put(2, "B");
底层流程:
-
会先比较 key 的大小(通过 compareTo() 或 Comparator);
-
插入到红黑树相应的位置;
-
自动执行必要的旋转或染色,保持平衡;
-
最终在中序遍历下,key 为有序输出。
四、源码片段:红黑树插入核心逻辑
TreeMap 中 put() 实际调用的是 putVal(),插入完成后会调用 balanceInsertion() 维护红黑树平衡:
private void balanceInsertion(TreeNode<K,V> x) {
x.red = true;
while (x != null && x != root && x.parent.red) {
if (x.parent == x.parent.parent.left) {
TreeNode<K,V> y = x.parent.parent.right;
// case 1: 叔叔是红色 -> 染色 + 向上递归
if (y != null && y.red) { ... }
// case 2/3: 叔叔是黑色 -> 旋转 + 染色
else {
if (x == x.parent.right) {
x = x.parent;
rotateLeft(x);
}
x.parent.red = false;
x.parent.parent.red = true;
rotateRight(x.parent.parent);
}
} else { ... } // 镜像对称操作
}
root.red = false;
}
红黑树插入时总是将新节点标红,然后根据父节点、叔叔节点颜色判断如何旋转/染色。
五、TreeSet 底层结构:其实就是 TreeMap
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable {
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>());
}
}
TreeSet 的每个元素被存储为 TreeMap 的 key,value 用一个哑元对象 PRESENT 代替,因此完全复用了 TreeMap 的自动排序和红黑树能力。
六、支持自定义排序逻辑
默认排序依赖元素自身实现的 Comparable 接口(自然顺序):
TreeMap<String, Integer> map = new TreeMap<>();
如果需要自定义排序规则,可传入 Comparator:
TreeMap<String, Integer> map = new TreeMap<>(
(a, b) -> b.compareTo(a) // 倒序排序
);
注意:Comparator 会覆盖元素本身的 compareTo() 实现。
七、常见误区与实践建议
✅ 保持 Key 的一致性:一旦插入,Key 的排序逻辑(compareTo 或 Comparator)就不能改变,否则查找/删除会失败。
❌ 不要使用不具备比较性的 Key 类型:否则将抛出 ClassCastException。
✅ 避免 null key:TreeMap 不允许 null key,但允许 null value;
❗ 红黑树插入不是稳定排序,不要期望插入顺序和遍历顺序一致。
八、TreeMap 的删除操作及红黑树重平衡
相比插入操作,删除节点对红黑树的结构破坏更大,因此恢复平衡的逻辑更复杂。
在 TreeMap.remove() 中,核心调用路径如下:
public V remove(Object key) {
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
deleteEntry(p);
return p.value;
}
在 deleteEntry() 中,如果删除的是红色节点,直接移除即可;但如果是黑色节点,就可能违反红黑树的黑色高度平衡规则,需要调用 fixAfterDeletion() 修复结构。
fixAfterDeletion()的核心处理逻辑包括:
-
节点兄弟是红色 ➜ 染色+旋转;
-
节点兄弟是黑色,但子节点有红色 ➜ 单/双旋转;
-
节点兄弟及其子节点全是黑 ➜ 染色+向上递归修复。
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
Entry<K,V> sib = rightOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else {
// 对称处理
}
}
setColor(x, BLACK);
}
九、红黑树旋转图解
左旋操作(rotateLeft):
右旋操作(rotateRight):
插入/删除过程中的这些操作,正是保证红黑树高度稳定在 log(n) 的关键。
十、TreeMap 与 HashMap 的对比选型
特性 | TreeMap | HashMap |
---|---|---|
底层结构 | 红黑树(有序) | 数组 + 链表 / 红黑树(无序) |
插入性能 | O(log n) | O(1) ~ O(log n)(高并发退化) |
查询性能 | O(log n) | O(1) ~ O(log n) |
Null Key 支持 | ❌ 不支持 | ✅ 支持一个 null key |
排序能力 | ✅ 支持(自然排序 / Comparator) | ❌ 无顺序 |
线程安全 | ❌ 非线程安全 | ❌ 非线程安全 |
✅ 选型建议:
-
若需顺序访问,且 key 可比较,选择 TreeMap;
-
若更关注性能和插入/查询效率,优先选择 HashMap;
-
并发场景使用 ConcurrentHashMap 或手动加锁。
十一、NavigableMap 高阶用法
TreeMap 实现了 NavigableMap 接口,因此支持许多“导航式”操作,常用于区间查找或范围定位。
TreeMap<Integer, String> map = new TreeMap<>();
map.put(1, "one");
map.put(3, "three");
map.put(5, "five");
map.lowerKey(4); // 3
map.floorKey(5); // 5
map.ceilingKey(2); // 3
map.higherKey(5); // null
map.subMap(2, 5); // key ∈ [2, 5)
这些操作背后的逻辑仍是基于红黑树结构进行定向查找,性能稳定在 O(log n)。
十二、结语
红黑树赋予了 TreeMap / TreeSet 强大的排序与范围查找能力,其复杂但高效的插入、删除和查找机制,是 Java 集合框架中不可或缺的核心之一。
了解其实现原理,有助于你:
-
做出合理的集合选型;
-
理解复杂度背后的本质;
-
写出更高性能、更可控的业务代码。