文章目录
一、简介
TreeMap
是按照 Key
的排序结果来组织内部结构的 Map
类集合,它改变了Map
类散乱无序的形象。
TreeMap
依靠Comparable
或Comparator
来实现Key
的去重。
public class TreeMapRepeat {
public static void main(String[] args) {
// 如果仅此处的 TreeMap 换成 HashMap,则 size = 1
TreeMap map = new TreeMap();
map.put(new Key(), "value one");
map.put(new Key(), "value two");
// TreeMap, size = 2, 因为 Key 去重规则是根据排序结果
System.out.println(map.size());
}
}
class Key implements Comparable<Key> {
// 返回负的常数,表示此对象永远小于收入的 other 对象,此处决定 TreeMap 的 size = 2
@Override
public int compareTo(Key other) {
return -1;
}
@Override
public int hashCode() {
return 1;
}
@Override
public boolean equals(Object obj) {
return true;
}
}
TreeMap
是线程不安全的集合,不能在多线程之间进行共享数据的写操作。
在多线程进行写操作时,需要添加互斥机制,或者把对象放在
Collections.synchroinzedMap(treeMap);
中同步
二、源码分析
(1)属性
如图:
(2)put()
增加
插入新节点之前,需要明确的三个前提条件:
- 需要调整的新节点总是红色的
- 如果插入新节点的父节点是黑色的,无序调整。
- 如果插入新节点的父节点是红色的,因为红黑树规定不能出现相邻的两个红色节点,所以进入循环判断,或重新着色,或左右旋转,最终达到红黑树的五个约束条件,退出条件如下:
while(x != null && x != root && x.parent.color == RED){...}
如图:
(3)fixAfterInsertion()
插入节点后动作
如图:
(4)remove()
删除
环境:JDK 8
public V remove(Object key) {
// 在树中找到节点
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
V oldValue = p.value;
// 删除节点
deleteEntry(p);
return oldValue;
}
private void deleteEntry(Entry<K,V> p) {
// 树的修改次数
modCount++;
// 节点数减少
size--;
// 若要删除的节点,左右子树不为null
if (p.left != null && p.right != null) {
// 找到继承者,然后简单值赋值
Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s; // 注意,现象 p = s
}
// 寻找代替节点(类似继承者)
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
// 若不为 null
if (replacement != null) {
// Link replacement to parent
replacement.parent = p.parent;
// 若要删除的节点的父节点为null,即 p 为根节点,替代这变成根节点
if (p.parent == null)
root = replacement;
// 找 p 的位置,是在父节点左边?
else if (p == p.parent.left)
p.parent.left = replacement;
else // 找 p 的位置,实在父节点的右边?
p.parent.right = replacement;
// Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null; // 解除依赖,方便GC回收
// Fix replacement
if (p.color == BLACK) // 如果删除的节点是黑色
fixAfterDeletion(replacement); // 重新着色和旋转操作
} else if (p.parent == null) { // return if we are the only node.
root = null;
} else { // No children. Use self as phantom replacement and unlink.
if (p.color == BLACK)
fixAfterDeletion(p);
if (p.parent != null) {
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
(5)rotateLeft()
左旋
如图:
(6)fixAfterDeletion()
删除节点后动作
fixAfterInsertion()
与 fixAfterDeletion()
原理基本相同。
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) { // 查询的节点,不是根节点且是黑色
if (x == leftOf(parentOf(x))) { // x 是 其父节点的左儿子嘛?
Entry<K,V> sib = rightOf(parentOf(x)); // 获取 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); // 把 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 { // symmetric(对称)
Entry<K,V> sib = leftOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}
if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}
setColor(x, BLACK);
}
(6)rotateRight()
右旋
private void rotateRight(Entry<K,V> p) {
// 节点不为null
if (p != null) {
// 获取 p 的左子树
Entry<K,V> l = p.left;
// p的左指针 链接到 p 的左子树的右子树
p.left = l.right;
if (l.right != null) l.right.parent = p; // 设置回溯链接
// 设置共同的父节点
l.parent = p.parent;
// 主要让 l 代替 p的位置
// 如果 p为 根节点
if (p.parent == null)
root = l;
else if (p.parent.right == p)
p.parent.right = l;
else p.parent.left = l;
l.right = p;
p.parent = l;
}
}
三、演示图
TreeMap<Integer, String> treeMap = new TreeMap<>();
treeMap.put(55, "fifty-five");
treeMap.put(56, "fifty-six");
treeMap.put(57, "fifty-seven");
treeMap.put(58, "fifty-eight");
treeMap.put(83, "eighty-three");
treeMap.remove(57);
treeMap.put(59, "fifty-nine");
步骤如图: