📅  最后修改于: 2023-12-03 15:16:19.304000             🧑  作者: Mango
Java中的TreeMap是一种基于红黑树数据结构实现的映射表。TreeMap的特点是可以保证键值对的有序性,同时增删改查的时间复杂度都可以做到O(logn)。
为了更好地理解TreeMap的内部工作原理,我们需要先来介绍一下红黑树。
红黑树是一种自平衡二叉搜索树,它保证了树的深度最多是其他二叉搜索树的1.5倍。红黑树中每个节点要么是黑色,要么是红色,同时满足以下规则:
在Java中,TreeMap的底层数据结构就是红黑树。TreeMap的元素被组织成一棵红黑树,每个元素在树中的位置由其键决定。TreeMap中的每个节点都包含一个键和一个值,键用于标识节点的位置,值则存储着与该键相关的数据。
当向TreeMap中插入一个键值对时,程序会先将该键值对封装成一个节点并将其插入到红黑树中。插入新节点时,程序会遵循以下规则:
具体的插入操作可以看下面的代码实现:
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// 利用二叉搜索树找到新节点的正确位置
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
} else {
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
// 插入新节点,并通过旋转和重着色操作保证红黑树的性质
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
删除操作的实现比插入操作要复杂一些。当从TreeMap中删除某个键值对时,程序会先找到该键对应的节点,然后根据节点的颜色和位置进行删除操作。
删除一个节点时,需要考虑以下几点:
具体的删除操作可以看下面的代码实现:
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--;
// 如果待删除节点有两个子节点,则找到其后继节点
if (p.left != null && p.right != null) {
Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
}
// 此时待删除节点最多只有一个子节点
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
// 如果待删除节点有一个子节点
if (replacement != null) {
// 把replacement接到p的父节点上
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement;
// 清理p的所有链接
p.left = p.right = p.parent = null;
// 如果待删除节点是黑色的,需要进行调整,避免破坏红黑树性质
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) {
// 删除的是根节点
root = null;
} else {
// 待删除节点没有子节点,并且本身是黑色的,需要进行调整。
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;
}
}
}
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 {
// symmetric
}
}
// 把根节点染黑
setColor(x, BLACK);
}
通过以上的介绍,我们可以清楚的理解TreeMap底层的实现原理以及数据结构的关系,同时我们也能深度理解红黑树的基本性质和相关操作。这些都是作为Java程序员需要了解的基本知识点,也是日常工作中所需要运用的。