尊重原创,转载请标明出处 http://blog.csdn.net/abcdef314159
Redis源码学习之字典:https://cloud.tencent.com/developer/article/1353754
在分析源代码之前,最好要标注出处,因为在Java中和Android中同一个类可能代码就会不一样,甚至在Android中不同版本之间代码也可能会有很大的差别,下面分析的是红黑树TreeMap,在\sources\android-25中。
红黑树的几个性质要先说一下,
1. 每个节点是红色或者黑色的。
2. 根节点是黑色的。
3. 每个叶节点的子节点是黑色的(叶子节点的子节点可以认为是null的)。
4. 如果一个节点是红色的,则它的左右子节点都必须是黑色的。
5. 对任意一个节点来说,从它到叶节点的所有路径必须包含相同数目的黑色节点。
TreeMap还有一个性质,就是他的左子树比他小,右子树比他大,这里的比较是按照key排序的。存放的时候如果key一样就把他替换了。

乍一看代码TreeMap有3000多行,其实他里面有很多内部类,有Values,EntrySet,KeySet,PrivateEntryIterator,EntryIterator,ValueIterator,KeyIterator,DescendingKeyIterator,
NavigableSubMap,AscendingSubMap,DescendingSubMap,SubMap,TreeMapEntry,TreeMapSpliterator,KeySpliterator,DescendingKeySpliterator,ValueSpliterator,
EntrySpliterator多达十几个内部类。其实很多都不需要了解,下面主要来看一下TreeMapEntry这个类,它主要是红黑树的节点
-
-
-
-
-
-
static final class TreeMapEntry<K,V> implements Map.Entry<K,V> {
-
-
-
TreeMapEntry<K,V> left = null;
-
TreeMapEntry<K,V> right = null;
-
TreeMapEntry<K,V> parent;
-
-
-
-
-
-
-
TreeMapEntry(K key, V value, TreeMapEntry<K,V> parent) {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
public V setValue(V value) {
-
-
-
-
-
-
public boolean equals(Object o) {
-
if (!(o instanceof Map.Entry))
-
-
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
-
-
return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
-
-
-
-
int keyHash = (key==null ? 0 : key.hashCode());
-
int valueHash = (value==null ? 0 : value.hashCode());
-
return keyHash ^ valueHash;
-
-
-
public String toString() {
-
return key + "=" + value;
-
-
既然是棵树,那么肯定就会有put方法以及remove方法,那么这里就从最简单的着手,先看一下put方法
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
public V put(K key, V value) {
-
TreeMapEntry<K,V> t = root;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
if (comparator != null) {
-
-
comparator.compare(key, key);
-
-
-
-
throw new NullPointerException("key == null");
-
} else if (!(key instanceof Comparable)) {
-
throw new ClassCastException(
-
"Cannot cast" + key.getClass().getName() + " to Comparable.");
-
-
-
-
root = new TreeMapEntry<>(key, value, null);
-
-
-
-
-
-
TreeMapEntry<K,V> parent;
-
-
Comparator<? super K> cpr = comparator;
-
-
-
-
-
cmp = cpr.compare(key, t.key);
-
-
-
-
-
-
return t.setValue(value);
-
-
-
-
-
throw new NullPointerException();
-
@SuppressWarnings("unchecked")
-
Comparable<? super K> k = (Comparable<? super K>) key;
-
-
-
cmp = k.compareTo(t.key);
-
-
-
-
-
-
return t.setValue(value);
-
-
-
TreeMapEntry<K,V> e = new TreeMapEntry<>(key, value, parent);
-
-
-
-
-
-
-
-
-
-
put方法存放的时候,首先是会存放到叶子节点,然后在进行调整。上面有一个重量级的方法fixAfterInsertion还没有分析,在分析fixAfterInsertion方法之前来看一下其他的几个方法,
-
private static <K,V> boolean colorOf(TreeMapEntry<K,V> p) {
-
-
return (p == null ? BLACK : p.color);
-
-
-
private static <K,V> TreeMapEntry<K,V> parentOf(TreeMapEntry<K,V> p) {
-
return (p == null ? null: p.parent);
-
-
-
private static <K,V> void setColor(TreeMapEntry<K,V> p, boolean c) {
-
-
-
-
-
private static <K,V> TreeMapEntry<K,V> leftOf(TreeMapEntry<K,V> p) {
-
return (p == null) ? null: p.left;
-
-
-
private static <K,V> TreeMapEntry<K,V> rightOf(TreeMapEntry<K,V> p) {
-
return (p == null) ? null: p.right;
-
下面再来看一下fixAfterInsertion方法
-
-
private void fixAfterInsertion(TreeMapEntry<K,V> x) {
-
-
-
-
-
-
-
while (x != null && x != root && x.parent.color == RED) {
-
-
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
-
TreeMapEntry<K,V> y = rightOf(parentOf(parentOf(x)));
-
-
-
-
-
-
-
setColor(parentOf(x), BLACK);
-
-
setColor(parentOf(parentOf(x)), RED);
-
-
x = parentOf(parentOf(x));
-
-
-
-
-
if (x == rightOf(parentOf(x))) {
-
-
-
-
-
-
setColor(parentOf(x), BLACK);
-
setColor(parentOf(parentOf(x)), RED);
-
rotateRight(parentOf(parentOf(x)));
-
-
-
TreeMapEntry<K,V> y = leftOf(parentOf(parentOf(x)));
-
-
-
-
-
setColor(parentOf(x), BLACK);
-
-
setColor(parentOf(parentOf(x)), RED);
-
x = parentOf(parentOf(x));
-
-
if (x == leftOf(parentOf(x))) {
-
-
-
-
setColor(parentOf(x), BLACK);
-
setColor(parentOf(parentOf(x)), RED);
-
rotateLeft(parentOf(parentOf(x)));
-
-
-
-
-
上面列出了6中可能,下面通过6张图来说明
下面是图1,不需要旋转,只需要调整颜色即可

下面是图2和图3,因为不平衡,所以需要旋转

下面是图4,和图1差不多,也分两种情况,一种是左节点一种是右节点

下面是图5和图6,因为不平衡,所以需要旋转

无论怎么旋转,他的左节点永远小于他,右节点永远大于他。通过不断的while循环,最终保证红黑树的平衡。下面来看一下旋转的方法,先看一下图

-
-
private void rotateLeft(TreeMapEntry<K,V> p) {
-
-
-
TreeMapEntry<K,V> r = p.right;
-
-
-
-
-
-
-
-
-
-
-
-
-
else if (p.parent.left == p)
-
-
-
-
-
-
-
-
-
而右旋方法rotateRight和左旋差不多,这里就不在分析。put方法分析完了,那么下一个就是remove方法了,
-
public V remove(Object key) {
-
-
-
TreeMapEntry<K,V> p = getEntry(key);
-
-
-
-
-
-
-
-
下面再看一下删除方法deleteEntry。
-
-
-
-
private void deleteEntry(TreeMapEntry<K,V> p) {
-
-
-
-
-
-
-
-
-
if (p.left != null && p.right != null) {
-
-
TreeMapEntry<K,V> s = successor(p);
-
-
-
-
-
-
-
-
-
-
TreeMapEntry<K,V> replacement = (p.left != null ? p.left : p.right);
-
-
if (replacement != null) {
-
-
-
-
-
replacement.parent = p.parent;
-
-
-
-
-
-
else if (p == p.parent.left)
-
p.parent.left = replacement;
-
-
p.parent.right = replacement;
-
-
-
-
p.left = p.right = p.parent = null;
-
-
-
-
-
-
-
fixAfterDeletion(replacement);
-
} else if (p.parent == null) {
-
-
-
-
-
-
-
-
-
-
-
-
-
else if (p == p.parent.right)
-
-
-
-
-
上面分析的时候有两个方法successor和fixAfterDeletion没有分析,下面先来看一下successor方法,这个方法很简单,其实就是返回大于节点p的最小值,看一下代码
-
-
-
-
static <K,V> TreeMapEntry<K,V> successor(TreeMapEntry<K,V> t) {
-
-
-
else if (t.right != null) {
-
TreeMapEntry<K,V> p = t.right;
-
-
-
-
-
-
-
TreeMapEntry<K,V> p = t.parent;
-
TreeMapEntry<K,V> ch = t;
-
-
-
-
while (p != null && ch == p.right) {
-
-
-
-
-
-
OK,下面再来看一下fixAfterDeletion方法,因为x所在分支少了一个黑色的节点,所以他的主要目的就是让x分支增加一个黑色节点。这个比fixAfterInsertion方法还难理解,看代码
-
-
private void fixAfterDeletion(TreeMapEntry<K,V> x) {
-
-
-
-
-
-
-
-
-
while (x != root && colorOf(x) == BLACK) {
-
-
if (x == leftOf(parentOf(x))) {
-
TreeMapEntry<K,V> sib = rightOf(parentOf(x));
-
-
-
-
-
-
if (colorOf(sib) == RED) {
-
-
setColor(parentOf(x), RED);
-
-
-
sib = rightOf(parentOf(x));
-
-
-
-
-
-
-
-
-
-
if (colorOf(leftOf(sib)) == BLACK &&
-
colorOf(rightOf(sib)) == BLACK) {
-
-
-
-
-
-
-
-
if (colorOf(rightOf(sib)) == BLACK) {
-
setColor(leftOf(sib), BLACK);
-
-
-
sib = rightOf(parentOf(x));
-
-
-
-
-
-
-
-
setColor(sib, colorOf(parentOf(x)));
-
setColor(parentOf(x), BLACK);
-
setColor(rightOf(sib), BLACK);
-
-
-
-
-
-
TreeMapEntry<K,V> sib = leftOf(parentOf(x));
-
if (colorOf(sib) == RED) {
-
-
setColor(parentOf(x), RED);
-
rotateRight(parentOf(x));
-
sib = leftOf(parentOf(x));
-
-
-
if (colorOf(rightOf(sib)) == BLACK &&
-
colorOf(leftOf(sib)) == BLACK) {
-
-
-
-
if (colorOf(leftOf(sib)) == BLACK) {
-
setColor(rightOf(sib), BLACK);
-
-
-
sib = leftOf(parentOf(x));
-
-
setColor(sib, colorOf(parentOf(x)));
-
setColor(parentOf(x), BLACK);
-
setColor(leftOf(sib), BLACK);
-
rotateRight(parentOf(x));
-
-
-
-
-
-
-
结合上面代码看一下下面的四张图




OK,到现在为止put和move方法都已经分析完了,下面看一下其他方法,
-
-
-
-
-
-
final TreeMapEntry<K,V> getFirstEntry() {
-
TreeMapEntry<K,V> p = root;
-
-
-
-
-
-
-
-
-
-
-
-
final TreeMapEntry<K,V> getLastEntry() {
-
TreeMapEntry<K,V> p = root;
-
-
-
-
-
再来看一下containsValue方法
-
-
-
public boolean containsValue(Object value) {
-
for (TreeMapEntry<K,V> e = getFirstEntry(); e != null; e = successor(e))
-
if (valEquals(value, e.value))
-
-
-
再来看一个getCeilingEntry,这个方法比较绕
-
-
-
-
-
-
-
-
final TreeMapEntry<K,V> getCeilingEntry(K key) {
-
TreeMapEntry<K,V> p = root;
-
-
int cmp = compare(key, p.key);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TreeMapEntry<K,V> parent = p.parent;
-
TreeMapEntry<K,V> ch = p;
-
while (parent != null && ch == parent.right) {
-
-
-
-
-
-
-
-
-
-
下面再来看一个和getCeilingEntry方法类似的方法getFloorEntry。
-
-
-
-
-
-
-
final TreeMapEntry<K,V> getFloorEntry(K key) {
-
TreeMapEntry<K,V> p = root;
-
-
int cmp = compare(key, p.key);
-
-
-
-
-
-
-
-
-
-
TreeMapEntry<K,V> parent = p.parent;
-
TreeMapEntry<K,V> ch = p;
-
-
-
-
-
-
-
while (parent != null && ch == parent.left) {
-
-
-
-
-
-
-
-
-
-
-
getHigherEntry函数和getCeilingEntry函数有点类似,不同点是如果有相同的key,getCeilingEntry会直接返回,而getHigherEntry仍然会返回比key大的最小节点,
同理getLowerEntry函数和getFloorEntry函数很相似,这里就不在详述。下面在看一个方法predecessor
-
-
-
-
static <K,V> TreeMapEntry<K,V> predecessor(TreeMapEntry<K,V> t) {
-
-
-
-
-
else if (t.left != null) {
-
TreeMapEntry<K,V> p = t.left;
-
-
-
-
-
-
-
-
-
TreeMapEntry<K,V> p = t.parent;
-
TreeMapEntry<K,V> ch = t;
-
while (p != null && ch == p.left) {
-
-
-
-
-
-
OK,目前为止TreeMap主要方法都已整理完毕。
参阅:Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例
TreeMap红黑树源码详解
原文:https://www.cnblogs.com/Chary/p/12217613.html