TreeMap源码阅读

73 阅读18分钟

我正在参加「掘金·启航计划」

TreeMap

TreeMap的内部结构是一棵红黑树,查找,添加 和删除的效率都是log(n);且内部所存储的元素都是有序的,为了使TreeMap中的元素是有序的,必须要提供一个能够比较TreeMap内部存储的元素的方法;TreeMap提供了两种方式来帮助TreeMap比较内部所存储的元素;

方式一:在创建TreeMap时,在构造方法中传入一个比较器Comparator

方式二:让被存入TreeMap的元素的键实现Comparable接口。

TreeMap的继承结构

  1. AbstractMap 实现了一些Map中的基本方法,减少了Map的实现类的工作量。
  2. NavigableMap 提供了一些返回TreeMap中与给定键值最接近的元素的方法。
  3. Serializable 序列化接口,对象持久化的方法。
  4. Cloneable 标记接口,表示对象可以被clone。

字段

 // 用来确定TreeMap中元素的顺序的比较器,如果为null,就调用被存储键的CompareTo方法。
 private final Comparator<? super K> comparator;
 // root根节点
 private transient Entry<K,V> root;
 // TreeMap中元素的个数
 private transient int size = 0;
 // TreeMap的修改次数。
 private transient int modCount = 0;

构造方法

 //创建一棵空树
 public TreeMap() {
     comparator = null;
 }
 // 创建一棵带有比较器的空树,向本TreeMap中添加元素都会都会按照这个比较器确定顺序。如果为null,就调用被存储键的CompareTo方法。
 public TreeMap(Comparator<? super K> comparator) {
     this.comparator = comparator;
 }
 // 根据传入的Map m构造一个包含m中所有元素新的map
 public TreeMap(Map<? extends K, ? extends V> m) {
     comparator = null;
     putAll(m);
 }
 ​
 // 根据传入的SortedMap m构造一个包含m中所有元素的新map,因为m是已排好序的map,
 //所以在向新map中拷贝元素时,不用再反复确认元素的顺序。可以提高创建新map的效率                         
 public TreeMap(SortedMap<K, ? extends V> m) {
     comparator = m.comparator();
     try {
         buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
     } catch (java.io.IOException cannotHappen) {
     } catch (ClassNotFoundException cannotHappen) {
     }
 }

常用方法

containsValue

整体思路:从最左侧节点开始,通过不断寻找后继节点遍历整棵红黑树,如果遍历完整棵红黑树没有找到指定的值,就返回false。

 // 判断TreeMap中有没有与value相同的值
 public boolean containsValue(Object value) {
     // 从最左边的元素开始遍历整棵树,如果树中有键值对的值与value相等,就返回true。
     for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
         if (valEquals(value, e.value))
             return true;
     return false;
 }
 ​
 // 返回树中第一个键值对,也就是最左侧的键值对
 final Entry<K,V> getFirstEntry() {
     Entry<K,V> p = root;    //从root开始
     if (p != null)
         while (p.left != null)  //一路向左
             p = p.left;
     return p;
 }
 ​
 // 返回指定节点的后继节点
 static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
     if (t == null)  //t是null的话,就没有后继节点,返回null。
         return null;
     else if (t.right != null) { // 如果节点t有右子树的话,那么t的后继节点为,右子树最左侧的元素。
         Entry<K,V> p = t.right;
         while (p.left != null)
             p = p.left;
         return p;
     } else {// 如果节点t没有右子树,
         // 1. 如果t是父节点的左子节点,直接返回父节点
         Entry<K,V> p = t.parent;
         Entry<K,V> ch = t;
         // 2. 如果t是父节点的右子节点,需要一直向上。直到ch是父亲节点的左子节点。
         while (p != null && ch == p.right) {
             ch = p;
             p = p.parent;
         }
         return p;
     }
 }
 ​
 // 判断o1,o2是否相等
 static final boolean valEquals(Object o1, Object o2) {
     // 增加了判空操作,防止出现空指针异常,o1,o2同时为null 返回true。
     return (o1==null ? o2==null : o1.equals(o2));
 }
 ​

get

整体思路:TreeMap是依靠Comparator的compare方法,或key实现Comparable接口中的compareTo方法 来确定元素的位置。

TreeMap优先Comparator的compare方法,如果没有比较器Comparator,再使用key的compareTo方法 来寻找元素。

 // 通过键 获取元素值
 public V get(Object key) {
     Entry<K,V> p = getEntry(key);
     // p等于null 就返回null,否则就返回元素值。
     return (p==null ? null : p.value);
 }
 ​
 // 通过键获取元素
 final Entry<K,V> getEntry(Object key) {
     // 如果比较器不为空,就调用getEntryUsingComparator,使用比较器判断元素位置。否则就用键的compareTo方法。
     if (comparator != null)
         return getEntryUsingComparator(key);
     // 如果键为空,就抛出空指针异常
     if (key == null)
         throw new NullPointerException();
     @SuppressWarnings("unchecked")
     // 将键强制转换为Comparable类型,如果键k没有实现Comparable接口,就会抛出异常。
         Comparable<? super K> k = (Comparable<? super K>) key;
     // 从根节点开始
     Entry<K,V> p = root;
     
     while (p != null) {
         int cmp = k.compareTo(p.key);
         if (cmp < 0)// 如果cmp>0, 说明目标key在当前节点的左侧
             p = p.left;
         else if (cmp > 0)// 如果cmp>0, 说明目标key在当前节点的右侧。
             p = p.right;
         else    // 如果cmp=0,说明当前节点就是要找的节点
             return p;
     }
     return null;
 }
 // 用比较器来查找元素位置
 final Entry<K,V> getEntryUsingComparator(Object key) {
     @SuppressWarnings("unchecked")
     K k = (K) key;
     Comparator<? super K> cpr = comparator;
     if (cpr != null) {
         Entry<K,V> p = root;
         while (p != null) {
             int cmp = cpr.compare(k, p.key);
             if (cmp < 0)// 如果cmp>0, 说明目标key在当前节点的右侧。
                 p = p.left;
             else if (cmp > 0)// 如果cmp>0, 说明目标key在当前节点的右侧。
                 p = p.right;
             else    // 如果cmp=0,说明当前节点就是要找的节点
                 return p;
         }
     }
     return null;    //mei'zhao'd
 }

put

整体思路是:如果根节点为空,就把新节点直接放在根结点上。

如果根节点不为空,再根据有无比较器,调用不同的比较方法,找到被插入节点的父节点位置。 最后插入新节点。

 // 向TreeMap中存放键值对
 public V put(K key, V value) {
     Entry<K,V> t = root;    //从根节点开始,寻找键值对存放的位置。
     if (t == null) {
         // 检查key是否可以比较。
         compare(key, key); // type (and possibly null) check
         root = new Entry<>(key, value, null);   // 如果根节点为null,直接将新创建的节点赋值给root
         size = 1;
         modCount++;
         return null;
     }
     int cmp;
     Entry<K,V> parent;
     // split comparator and comparable paths
     Comparator<? super K> cpr = comparator;
     // 如果有比较器,就采用比较器比较,没有的话,就采用compareTo方法比较。
     if (cpr != null) {
         // 循环查找查入元素的父节点位置
         do {
             parent = t;
             cmp = cpr.compare(key, t.key);
             if (cmp < 0)    // 如果cmp<0,说明目标key在当前节点的左侧。
                 t = t.left;
             else if (cmp > 0)   // 如果cmp>0,说明目标key在当前节点的右侧。
                 t = t.right;
             else    // 如果cmp=0,说明当前节点的key,就是目标key。 目标key在TreeMap中已经存在,只需要更新当前节点的value即可。
                 return t.setValue(value);
         } while (t != null);
     }
     else {  // 使用key的compareTo方法比较
         if (key == null)    // 不能出现空键
             throw new NullPointerException();
         @SuppressWarnings("unchecked")
         // 将key强制转换为Comparable类型,如果key没有实现Comparable接口,就会报错
             Comparable<? super K> k = (Comparable<? super K>) key;
         // 循环遍历搜索插入元素的父节点位置
         do {
             parent = t;
             cmp = k.compareTo(t.key);
             if (cmp < 0)    // 如果cmp<0,说明目标key在当前节点的左侧。
                 t = t.left;
             else if (cmp > 0)   // 如果cmp>0,说明目标key在当前节点的右侧。
                 t = t.right;
             else    // 如果cmp=0,说明当前节点的key,就是目标key。 目标key在TreeMap中已经存在,只需要更新当前节点的value即可。
                 return t.setValue(value);
         } while (t != null);
     }
     // 根据key和value创建新节点
     Entry<K,V> e = new Entry<>(key, value, parent);
     if (cmp < 0)    // 如果cmp<0, 把新节点连接在父节点的左侧
         parent.left = e;
     else    // 如果cmp>0, 把新节点连接在父节点的右侧
         parent.right = e;
     // 修复插入新节点后的红黑树
     fixAfterInsertion(e);
     // 元素个数+1
     size++;
     // 操作数+1;
     modCount++;
     // 如果是插入元素,就返回null,如果是替换元素,就返回被替换的值
     return null;
 }

putAll

整体思路:基于有序map的特性,TreeMap中提供了buildFromSorted方法,用于从一个有序的SortedMap中向本TreeMap中添加多个元素, 但要求本TreeMap必须是空树。

如果TreeMap是空树,就调用buildFromSorted方法,buildFromSorted方法的效率更高,否则就调用AbstractMap中putAll方法,AbstractMap中putAll方法就是反复调用put方法,效率比较低。

buildFromSorted方法,采用递归的方式创建红黑树,每次递归都会创建一个位于指定范围内的最中间的节点。并且再次递归创建左子树和右子树。

buildFromSorted方法还将位于最底层的元素全部染为红色,其他节点都是黑色,这样做的目的是,防止最后一层的元素未满,导致黑高不平衡。

 // 将参数map中的元素全部添加到本TreeMap中
 public void putAll(Map<? extends K, ? extends V> map) {
     // 参数map中的元素个数
     int mapSize = map.size();
     // buildFromSorted是一个专门针对从SortedMap向本TreeMap中添加元素而设计的方法,它比AbstractMap中的putAll方法更加高效。
     // 本TreeMap必须是空树,而且参数map必须是SortedMap的子类,才会调用buildFromSorted方法。否则就调用AbstractMap中的putAll方法
     if (size==0 && mapSize!=0 && map instanceof SortedMap) {
         Comparator<?> c = ((SortedMap<?,?>)map).comparator();
         // 若参数map中有比较器Comparator,则本TreeMap中的比较器必须与参数map中的比较器相同,要么都为空。否则无法执行添加操作。
         if (c == comparator || (c != null && c.equals(comparator))) {
             // 操作数++
             ++modCount;
             try {
                 // 参数:mapSize map的大小,map.entrySet().iterator() map的迭代器。
                 buildFromSorted(mapSize, map.entrySet().iterator(),
                                 null, null);
             } catch (java.io.IOException cannotHappen) {
             } catch (ClassNotFoundException cannotHappen) {
             }
             return;
         }
     }
     // 使用AbstractMap中的putAll方法,就是反复调用put方法
     super.putAll(map);
 }
 // 从SortedMap中向本集合中添加元素的方法。
 private void buildFromSorted(int size, Iterator<?> it,
                              java.io.ObjectInputStream str,
                              V defaultVal)
     throws  java.io.IOException, ClassNotFoundException {
     // 修改本TreeMap的元素个数。
     this.size = size;
     // 真正构造红黑树的方法,下面再展开讲。
     root = buildFromSorted(0, 0, size-1, computeRedLevel(size),
                            it, str, defaultVal);
 }
 // 计算出由sz个元素组成的红黑树有几层,最后被创建出来的红黑树最后一层全部要染成红色。
 private static int computeRedLevel(int sz) {
     int level = 0;
     for (int m = sz - 1; m >= 0; m = m / 2 - 1)
         level++;
     return level;
 }
 /**
 *   int level: 当前构造的节点是红黑树的第几层。
 *   int lo, int hi 从map的下标lo~hi的位置,构造一棵子树。
 *   int redLevel 红色节点所在的层。
 *   Itertor<?> it map的迭代器。
 *   java.io.ObjectInputStream str 对象输出流。
 *   V defaultVal 默认值
 *   构造策略:
 *   它会将迭代器中最中间的元素作为根节点 并返回。并且递归地创建左子树 和 右子树。
 */
 private final Entry<K,V> buildFromSorted(int level, int lo, int hi,
                                          int redLevel,
                                          Iterator<?> it,
                                          java.io.ObjectInputStream str,
                                          V defaultVal)
     throws  java.io.IOException, ClassNotFoundException {
     // 如果高位下标大于低位下标 就返回null。
     if (hi < lo) return null
     // 计算位于中间元素的下标。无符号右移,相当于整除2;
     int mid = (lo + hi) >>> 1
     Entry<K,V> left  = null;
     // 递归地创建左子树
     if (lo < mid)
         left = buildFromSorted(level+1, lo, mid - 1, redLevel,
                                it, str, defaultVal
     // extract key and/or value from iterator or stream
     K key;
     V value;
     // 如果迭代器不为空,就使用迭代器获取键值对,如果迭代器为空,就使用对象输出流获取键值对
     if (it != null) {
         // 判断参数defaultVal是否为空,空的话使用迭代器中的键值对的值,不为空的话,使用defaultVal作为新创建节点的值。
         if (defaultVal==null) {
             // 迭代器获取键值对对象
             Map.Entry<?,?> entry = (Map.Entry<?,?>)it.next();
             key = (K)entry.getKey();
             value = (V)entry.getValue();
         } else {
             key = (K)it.next();
             // 使用默认值作为新节点的值
             value = defaultVal;
         }
     } else { // use stream
         // 从对象输入流中获取对象。
         key = (K) str.readObject();
         value = (defaultVal != null ? defaultVal : (V) str.readObject());
     }
     // 创建节点。
     Entry<K,V> middle = new Entry<>(key, value, null);
     // color nodes in non-full bottommost level red
     // 将最底层的节点染为红色,这也做是为了保持黑高。
     if (level == redLevel)
         middle.color = RED;
     // 连接左子树
     if (left != null) {
         middle.left = left;
         left.parent = middle;
     // 递归创建右子树
     if (mid < hi) {
         Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel,
                                            it, str, defaultVal);
         middle.right = right;
         right.parent = middle;
     // 返回本层递归创建的节点。
     return middle;
 }
 ​

remove

整体思路:先调用getEntry方法,判断该键值对是否存在。如果存在再调用deleteEntry方法进行删除,删除时,若被删除节点不是叶子节点,就找到被删除节点的后继节点,交换删除节点与被删除节点的键和值,再删除后继节点代替删除被删除节点。

 //移除指定节点,并返回被移除节点的值
 public V remove(Object key) {
     // 先判断TreeMap中是否存在该key。不存在就直接返回null
     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 strictly internal, copy successor's element to p and then make p
     // point to successor.
     //如果被删除节点不是叶子节点,就寻找他的后继节点,并交换后继节点与被删除节点的键和值。
     if (p.left != null && p.right != null) {
         Entry<K,V> s = successor(p);
         p.key = s.key;
         p.value = s.value;
         p = s;
     } // p has 2 children
     // Start fixup at replacement node, if it exists.
     // p点被删除后,replacement将取代p节点的位置。replacement为p的左子节点或右子节点
     Entry<K,V> replacement = (p.left != null ? p.left : p.right);
     // 如果replacement不为null,就将replacement连接到p的位置,如果replacement为null,就直接将连接到p的引用都置为null。
     if (replacement != null) {
         // 修改replacement的parent
         replacement.parent = p.parent;
         // 如果p的parent为null,说明p是根节点,直接将replacement赋值给root。
         if (p.parent == null)
             root = replacement;
         // 将原本指向p的引用修改为指向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) { // 如果p是TreeMap中唯一一个元素,直接将root=null即可。
         root = null;
     } else { //  如果p是叶子节点,就先修复,再删除这个叶子节点
         // 如果p是黑色,就需要修复红黑树。
         if (p.color == BLACK)
             fixAfterDeletion(p);
         // 将指向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;
         }
     }
 }
 ​

rotateLeft 左旋

左旋、右旋 和 变色是红黑树保持性质的三种基本操作。

我在上一篇博客中已经梳理了红黑树插入 和删除的不同情况下的处理方法,跟官方源码的处理方法也大致相同。只不过官方源码更加简洁,易懂;所以再来学习一下。

  /**
  *          p                      p     
  *          |                      |     
  *          n                      y     
  *        /  \      --->         /  \ 
  *       x    y                 n    ry   
  *           / \              /  \       
  *         ly   ry           x   ly     
  * 左旋操作
  * 1. 将y的左子节点ly连接到n右子节点上
  * 2. 修改ly的父节点指向n
  * 3. 将n连接到y的左子节点位置上
  * 4. 修改n的parent指定y
  * 5. 修改y的parent指向n的父节点
  * 6. 修改p本指向n的子节点指向y
  */
 // 左旋
 private void rotateLeft(Entry<K,V> p) {
     // 判空 避免空指针。
     if (p != null) {
         // r为p的右子节点     左旋后,r将成为p的父亲节点。p将成为r的左子节点。
         Entry<K,V> r = p.right;
         // 将r的左子节点连接到p的右子节点上。
         p.right = r.left;
         // 同时要修改r的左节点的父亲节点。
         if (r.left != null)
             r.left.parent = p;
         // 修改r的parent 为 p的parent
         r.parent = p.parent;
         // 如果p的parent为null,直接将root改为r。
         if (p.parent == null)
             root = r;
         else if (p.parent.left == p)    // 如果p的parent不为null,修改原p的父亲节点指向p的指针修改为指向r。
             p.parent.left = r;
         else
             p.parent.right = r;
         r.left = p;     // p成为r的左子节点
         p.parent = r;   
     }
 }
 ​
 ​

rotateRight 右旋

 /**
  *           p                  p
  *           |                  |
  *           n                  x
  *         /  \               /  \
  *        x    y  --->      lx    n
  *      /  \                     / \
  *    lx   rx                  rx   y
  *
  *
  * 右旋操作
  * 1. 将x的右子节点rx连接到n左子节点上
  * 2. 修改rx的父节点指向n
  * 3. 将n连接到x的右子节点位置上
  * 4. 修改n的parent指定x
  * 5. 修改x的parent指向n的父节点
  * 6. 修改p本指向n的子节点指向x
  */
 private void rotateRight(Entry<K,V> p) {
     // 判空 避免空指针异常
     if (p != null) {
         // l为p的左子节点     右旋后,p的左子节点l将成为p的父亲节点,p将成为l的右子节点。
         Entry<K,V> l = p.left;
         // 将l的右子节点连接到p的左子节点上。
         p.left = l.right;
         // 同时修改l的右子节点的parent指针。
         if (l.right != null) 
             l.right.parent = p;
         // 修改l的parent 指向p的parent。
         l.parent = p.parent;
         // 如果p的parent为null,直接将l赋值给root
         if (p.parent == null)
             root = l;
         else if (p.parent.right == p)   // 如果p的parent不为null,修改原p的父亲节点指向p的指针修改为指向r。
             p.parent.right = l;
         else p.parent.left = l;
         l.right = p;    // p成为l的右子节点。
         p.parent = l;
     }
 }

fixAfterInsertion

修复因节点操作导致的红黑树不平衡。

插入节点导致的问题情况有以下几种情况。

  1. root为空,直接将root指向新节点。
  2. 父亲节点是黑色,直接插入,无影响。
  3. 键值已存在,更新键值对的值。
  4. 如果父节点是红色,因为插入的节点是红色,红红不能相连,就需要处理 4.1. 叔叔节点为红色 将父亲节点和叔叔节点染黑。爷爷节点染红。 4.2. 叔叔结点为黑色或为空 4.2.1. 父节点在爷爷节点左侧,子节点在父节点左侧(LL双红) 将父亲节点染黑。爷爷节点染红。对爷爷节点右旋。 4.2.2. 父节点在爷爷节点左侧,子节点在父节点右侧(LR双红) 对父亲节点左旋 然后转-->4.2.1. 4.2.3. 父节点在爷爷节点右侧,子节点在父节点右侧(RR双红) 将父亲节点染黑。爷爷节点染红。对爷爷节点左旋。 4.2.4. 父节点在爷爷节点右侧,子节点在父节点左侧(RL双红)、 对父亲节点右旋 然后转-->4.2.3.

可以看到,前三种情况在插入节点阶段就能解决。需要修复的只有情况4

 private void fixAfterInsertion(Entry<K,V> x) {
     // 被插入节点默认都是红色。
     x.color = RED;
     // 如果x的父节点是红色,就需要修复,如果修复一直上溢到根节点root,就得退出,因为无法继续向上修复了。
     while (x != null && x != root && x.parent.color == RED) {
         // 父亲节点是祖父节点的左子节点
         if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
             // y是被插入节点的叔叔节点。
             Entry<K,V> y = rightOf(parentOf(parentOf(x)));
             // 叔叔节点是红色 4.1 的情况
             if (colorOf(y) == RED) {
                 // 将父亲节点染黑
                 setColor(parentOf(x), BLACK);
                 // 叔叔节点染黑
                 setColor(y, BLACK);
                 // 祖父节点染红
                 setColor(parentOf(parentOf(x)), RED);
                 // 再将祖父节点当作被插入节点继续向上修复。
                 x = parentOf(parentOf(x));
             } else {
                 //4.2.2父节点在爷爷节点左侧,被插入节点在父节点右侧
                 if (x == rightOf(parentOf(x))) {
                     // 这里需要对父亲节点左旋,但是左旋后,原来的父亲节点就成为了当前节点的左子节点。所以这里直接将当前节点x的父亲节点赋值给x,再对x左旋,把x当成被插入节点。这时情况就转变成了4.2.1.
                     x = parentOf(x);
                     rotateLeft(x);
                 }
                 // 4.2.1. 父节点在爷爷节点左侧,被插入节点在父节点左侧
                 // 将父亲节点染为黑色。
                 setColor(parentOf(x), BLACK);
                 // 祖父节点染为红色
                 setColor(parentOf(parentOf(x)), RED);
                 // 再对祖父节点右旋
                 rotateRight(parentOf(parentOf(x)));
             }
         } else { // 父亲节点是祖父节点的右子节点,操作与上面操作对称。
             Entry<K,V> y = leftOf(parentOf(parentOf(x)));
             if (colorOf(y) == RED) {
                 setColor(parentOf(x), BLACK);
                 setColor(y, BLACK);
                 setColor(parentOf(parentOf(x)), RED);
                 x = parentOf(parentOf(x));
             } else {
                 if (x == leftOf(parentOf(x))) {
                     x = parentOf(x);
                     rotateRight(x);
                 }
                 setColor(parentOf(x), BLACK);
                 setColor(parentOf(parentOf(x)), RED);
                 rotateLeft(parentOf(parentOf(x)));
             }
         }
     }
     root.color = BLACK;
 }

fixAfterDeletion

修复因删除节点造成的红黑树不平衡。

删除节点造成的情况如下所示。

  1. 被删除节点是红色 这种情况不影响红黑树的性质,可以直接删除
  2. 被删除节点是黑色 2.1. 兄弟节点是黑色,且兄弟节点有红色的子节点 2.1.1. 兄弟节点的左子节点是红色 此时父亲节点的右子树为空,左子树为ll(兄弟节点在父亲节点的左侧,兄弟节点的左子节点在兄弟节点的左侧 left-left), 调整方法:将兄弟节点的颜色染为原来父亲节点的颜色,再将兄弟节点的左子节点和父亲节点染为黑色, 再对父亲节点右旋。 这样此子树的黑高就与删除前的黑高相等,此子树与删除前的子树相比,少了一个红色节点。 2.1.2. 兄弟节点的右子节点是红色 此时父亲节点的右子树为空,左子树为lR(left-right) 对兄弟节点进行左旋,并交换兄弟节点与兄弟节点的右子节点的颜色,就可转换为2.1.1 的情况 2.2.兄弟节点是黑色,且兄弟节点无子节点 2.2.1. 父节点是红色 将父亲节点染为黑色,兄弟节点染为红色即可 2.2.2. 父节点是黑色 这种情况下,被删除节点、父亲节点、和兄弟节点都是黑色,被删除节点被删除后,不能在以父节点为根节点的范围内,保持黑高不变, 就需要扩大调整的范围。 具体操作为:将兄弟节点染为红色(以父节点为根节点的子树黑高减一),再以父亲节点为删除节点,修复红黑树。 2.3. 兄弟节点是红色(父节点必定为黑色(性质4),且必定有黑色子节点,否则黑高不平衡(性质5)) 父亲节点染为红色,兄弟节点染为黑色,再对父亲节点右旋,就转变成了其他情况。
 private void fixAfterDeletion(Entry<K,V> x) {
     // 被删除节点是黑色就需要修复,因为影响黑高。
     while (x != root && colorOf(x) == BLACK) {
         // 被删除节点x是父亲节点的左子节点
         if (x == leftOf(parentOf(x))) {
             // sib是被删除节点的兄弟节点。
             Entry<K,V> sib = rightOf(parentOf(x));
             // 2.3. 兄弟节点是红色
             if (colorOf(sib) == RED) {
                 // 将兄弟节点染为黑色。
                 setColor(sib, BLACK);
                 // 父亲节点染为红色
                 setColor(parentOf(x), RED);
                 // 对父亲节点左旋
                 rotateLeft(parentOf(x));
                 // 更新兄弟节点 sib, 转变成其他情况继续修复。
                 sib = rightOf(parentOf(x));
             }
             // 2.2.兄弟节点是黑色,且兄弟节点无子节点。   空节点也是黑色节点。
             // 2.2.1 和 2.2.2 的情况可以合并。
             if (colorOf(leftOf(sib))  == BLACK &&
                 colorOf(rightOf(sib)) == BLACK) {
                 // 将兄弟节点染为红色
                 setColor(sib, RED);
                 // 将父节点赋值给x,继续向上修复红黑树
                 x = parentOf(x);
             } else {
                 // 2.1.2.兄弟节点是黑色, 兄弟节点的右子节点是红色
                 if (colorOf(rightOf(sib)) == BLACK) {
                     // 将兄弟节点的左子节点染为黑色。
                     setColor(leftOf(sib), BLACK);
                     // 兄弟节点染为红色。
                     setColor(sib, RED);
                     // 对兄弟节点右旋。
                     rotateRight(sib);
                     // 更新兄弟节点sib,情况就转变成了2.1.1
                     sib = rightOf(parentOf(x));
                 }
                 //   2.1.1. 兄弟节点是黑色, 兄弟节点的左子节点是红色.
                 // 把兄弟节点的颜色染为父亲节点的颜色。
                 setColor(sib, colorOf(parentOf(x)));
                 // 父亲节点染为黑色。
                 setColor(parentOf(x), BLACK);
                 // 将兄弟节点染为黑色。
                 setColor(rightOf(sib), BLACK);
                 // 对父节点左旋
                 rotateLeft(parentOf(x));
                 // 结束循环。
                 x = root;
             }
         } else { // 跟上面一样,是对称的。
             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);
 }

\