util包源码(四):TreeMap源码笔记

阅读时间:2019.1.18–1.23

一、概述

TreeMap底层为红黑树实现,最大的特点是可排序或者说天生有序。

  • 关于排序:排序的方式可以按大小,也可以自定义实现Comparator ,且对于自定义的类(如User类),那么必须自己定义比较机制
    1 、方式一:User类去实现java.lang.Comparable接口,并实现其compareTo()方法。
    2、方式二:写一个类(如MyCompatator)去实现java.util.Comparator接口,并实现compare()方法,然后将MyCompatator类实例对象作为TreeMap的构造方法参数进行传参(当然也可以使用匿名内部类),这些比较方法是怎么被调用的将在源码中讲解。
  • 关于红黑树:红黑树首先是一颗二叉树,而且它不是完全平衡的(非AVL),相反,它是降低了对平衡性的要求,从而在增删中减少了旋转次数,提升了增删性能的同时,也没有降低查找的速度。他有五大性质:
    性质1. 节点是红色或黑色。
    性质2. 根节点是黑色。
    性质3. 每个叶节点(NIL节点,空节点)是黑色的。
    性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
    性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
    在这里插入图片描述
二、树节点

可以看到,每个初始化的节点,都是黑色的

    static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left;//左子树
        Entry<K,V> right;//左子树
        Entry<K,V> parent;//上层节点
        boolean color = BLACK;//初始为黑

        Entry(K key, V value, Entry<K,V> parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }
        //方法省略
    }
三、重要属性
private final Comparator<? super K> comparator;//外部比较器,红黑树有序的基础;若为空则自然有序
private transient Entry<K,V> root;//根节点
private transient int size = 0;//节点总数
private transient int modCount = 0;
private transient EntrySet entrySet;
private transient KeySet<K> navigableKeySet;
private transient NavigableMap<K,V> descendingMap;//倒序的map
四、初始化方法

TreeMap提供四种初始化方式:
如果节点内是自定义类型对象,则必须要么有外部比较器Comparator,要么有内部比较器Comparable
TreeMap():默认比较器
TreeMap(Comparator<? super K> comparator) :需要自定义比较器
TreeMap(Map<? extends K, ? extends V> m):用其他map作为参数,但value必须实现比较器
TreeMap(SortedMap<K, ? extends V> m):用已经排好序的map作为参数
下面分别对其进行分析

//使用自然排序法,key必须可以进行比较,例如int,float,String;或者树节点Entry<K,V>的K实现了Comparable接口
public TreeMap() {
    comparator = null;
}

//Entry<K,V>的K可以不实现Comparable,则传参必须实现Comparator
public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
        //例如:TreeMap< UserB, Integer> treeMapB = new TreeMap<UserB, Integer>((o1,o2)->(o1.getId()<o2.getId())?-1:1);
}



//接收一个
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) {
    }
}

下面的初始化方法的参数是map,map的key必须是自然可排序的

public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);//把指定map的元素都复制到本map里,是一个建树的过程
}

public void putAll(Map<? extends K, ? extends V> map) {
    int mapSize = map.size();
    if (size==0 && mapSize!=0 && map instanceof SortedMap) {
        Comparator<?> c = ((SortedMap<?,?>)map).comparator();
        if (c == comparator || (c != null && c.equals(comparator))) {
            ++modCount;
            try {
            	//调用下面的入口方法buildFromSorted
                buildFromSorted(mapSize, map.entrySet().iterator(),
                                null, null);
            } catch (java.io.IOException cannotHappen) {
            } catch (ClassNotFoundException cannotHappen) {
            }
            return;
        }
    }
    super.putAll(map);
}

下面的初始化方法的参数也是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) {
    }
}
private void buildFromSorted(int size, Iterator<?> it,
                              java.io.ObjectInputStream str,
                              V defaultVal)
     throws  java.io.IOException, ClassNotFoundException {
     this.size = size;
     root = buildFromSorted(0, 0, size-1, computeRedLevel(size),
                            it, str, defaultVal);
 }

上述两个方法的核心都是buildFromSorted,这个方法是结果就是建立红黑树。我们细致的解读这个方法。
主要思想:(翻译原意)根节点是最中心的节点。为了实现这一目的,我们必须首先递归的构建整个左子树,然后剩下的元素构建成右子树。
参数含义:level:当前所在层次;lo:子树的第一个索引位置;hi:子树的最后一个索引位置

参数定义参数含义
int level当前所在层次
int lo子树的第一个索引位置
int hi子树的最后一个索引位置
int redLevel使用最佳的红节点层级computeRedLevel
Iterator<?> it遍历每个节点的迭代器
java.io.ObjectInputStream str如果没有迭代器,用readObject 来读取节点也可以,默认用的迭代器
V defaultVal用默认值填充新节点,若为空,用原map的节点填充

红黑树不唯一,红节点越多,树的操作越复杂,红节点最优层的算法如下:

private static int computeRedLevel(int sz) {
    int level = 0;
    for (int m = sz - 1; m >= 0; m = m / 2 - 1)
        level++;
    return level;
}

现在我们来解析buildFromSorted这个递归算法:
步骤一:先求出mid=(lo+hi)>>1,左子树即lo < mid的部分,进行逐层递归
buildFromSorted(level+1, lo, mid - 1, redLevel, it, str, defaultVal);直到到达叶节点(lo=mid)
步骤二:创建叶节点Entry<K,V> middle = new Entry<>(key, value, null);
步骤三:染色。如果当前层级==红色所在层级redLevel,涂红
步骤四:挂接左子树
步骤五:构建右子树,然后挂接到右侧

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 {
        
        if (hi < lo) return null;
		//步骤一:先求出mid=(lo+hi)>>1,左子树即lo < mid的部分,进行逐层递归
        int mid = (lo + hi) >>> 1;

        Entry<K,V> left  = null;
        if (lo < mid)
            left = buildFromSorted(level+1, lo, mid - 1, redLevel,it, str, defaultVal);
        K key;
        V value;
        //步骤二:创建叶节点Entry<K,V> middle =  new Entry<>(key, value, null);
        if (it != null) {
            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);
		//步骤三:染色。如果当前层级==红色所在层级redLevel,涂红
        // 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;
    }

经过buildFromSorted之后得到了一颗红黑树

五、重要public方法

增删操作会影响树的平衡,要进行旋转,所以增删要仔细分析

功能性方法
public int size()//
public boolean containsKey(Object key)
public boolean containsValue(Object value)
public Comparator<? super K> comparator()
public void putAll(Map<? extends K, ? extends V> map)
public Object clone()
“增”类方法
public V put(K key, V value)
“删”类方法
public V remove(Object key)
public void clear()
“改”类方法
public V replace(K key, V value)
public boolean replace(K key, V oldValue, V newValue)
“查”类方法
public V get(Object key)
public K firstKey()
public K lastKey()
public Map.Entry<K,V> firstEntry()
public Map.Entry<K,V> lastEntry()
public Map.Entry<K,V> pollFirstEntry()
public Map.Entry<K,V> pollLastEntry()
public Map.Entry<K,V> lowerEntry(K key)
public Map.Entry<K,V> ceilingEntry(K key)
public Map.Entry<K,V> higherEntry(K key)
public K lowerKey(K key)
public K higherKey(K key)
public K ceilingKey(K key)
public Map.Entry<K,V> floorEntry(K key)
函数式方法
public void forEach(BiConsumer<? super K, ? super V> action)
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
1.功能性方法

1)containsKey:查找map中是否存在key,遍历所有key。如果有外部比较器,使用外部比较器查找;如果没有,用自然序查找该key,小于当前节点,找左子树,大于当前节点,找右子树

    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }
    
    final Entry<K,V> getEntry(Object key) {
        // Offload comparator-based version for sake of performance
        if (comparator != null)
            return getEntryUsingComparator(key);
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
        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)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
    }

2)containsValue:查找map中是否有对应的value这个for循环很关键。起始是树的最小节点,增加条件是顺序取下一个节点successor(e)按这样的顺序:目前节点是否有右节点?有则指向右节点的最小左节点;如果没有,取上一层(parent)节点。
真正的取value,判断value过程很简单

    public boolean containsValue(Object value) {
        for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
            if (valEquals(value, e.value))
                return true;
        return false;
    }
    //取map中最小的节点,即左子树的最下层左节点
    final Entry<K,V> getFirstEntry() {
        Entry<K,V> p = 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)//已经到最上层了,返回空
            return null;
        else if (t.right != null) {//在树的右子树中找是否有要找的节点(因为先左再中再右,故只剩右)
            Entry<K,V> p = t.right;
            while (p.left != null)
                p = p.left;
            return p;
        } else {//右子树中也没有要找的,后退一层
            Entry<K,V> p = t.parent;
            Entry<K,V> ch = t;
            while (p != null && ch == p.right) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }
    //判断两者是否一致
    static final boolean valEquals(Object o1, Object o2) {
        return (o1==null ? o2==null : o1.equals(o2));
    }

3)putAll已在初始化方法中详尽的分析了

2.“增”类方法

put方法把<key,value>插入到树中,整体分为三步:
第一步:找到应插入的位置。同样的,如果有外部比较器,就用外部比较器进行排序;如果没有外部比较器Comparator,就用内部比较器Comparable。找插入位置时,分以下情况:当遍历树节点t时,如果t.key<key,则往左子树找;若t.key>key,则往右子树找;如果t.key==key,则替换掉原值。前两种情况可能需要进行再平衡,最后一种情况不需要再平衡,直接返回
第二步:插入节点
第三步:再平衡红黑树

    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) {//用外部比较器Comparator找插入的位置
            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);//如果相等,直接把新value赋值过去,不需要再平衡
            } while (t != null);
        }
        else {//用内部比较器Comparable找插入的位置
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                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);//如果相等,直接把新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;
    }

再平衡红黑树fixAfterInsertion,这里有个地方很有意思。我们知道在创建节点时,我们的节点时黑的boolean color = BLACK;这是因为默认创建的是叶子节点NIL,一定必须是黑的。但是当要进行插入树中时,他变成了节点,先把他染红。实际是可以用黑节点插入的,但它需要旋转的次数会多。所以先染红。对于如何调整红黑树,当三种情况时,需要调整。

三个case都要满足的条件:(x不是叶节点 && x不是根节点 && x的父节点是红的)
在这里插入图片描述
此时,违背原则4(不能有两个连续的红节点),且以下假定了X的父节点是左子树,右子树时是一样的
case 1:如果当X的父节点的兄弟节点也为红时,采用以下方法来重新平衡,设父节点的兄弟节点为Y,操作步骤如下:

  • 将父节点染黑 setColor(parentOf(x), BLACK);
  • 将父亲的兄弟节点染黑 setColor(y, BLACK);
  • 将父亲的父亲节点染红 setColor(parentOf(parentOf(x)), RED);
  • 此时父亲的父亲节点至根节点可能再次不平衡,所以继续把X指向祖父节点进行再平衡x = parentOf(parentOf(x));
    在这里插入图片描述
    抽出来的代码如下:
private void fixAfterInsertion(Entry<K,V> x) {
   while (x != null && x != root && x.parent.color == RED) {//如果当X的父节点的兄弟节点也为红时
   	   //...省略代码
       Entry<K,V> y = rightOf(parentOf(parentOf(x)));
       if (colorOf(y) == RED) {
           setColor(parentOf(x), BLACK);
           setColor(y, BLACK);
           setColor(parentOf(parentOf(x)), RED);
           x = parentOf(parentOf(x));
       }
       //...省略代码
}

case 2:当父节点的兄弟节点是黑色,并且X是父节点的右子节点。操作步骤如下:

  • 把X的父节点作为操作节点 x = parentOf(x);
  • 左旋转 rotateLeft(x);
  • 转化为case 3,再通过case 3进行调整
    在这里插入图片描述
    在case 3中再给出剥离后的代码

case 3:当父节点的兄弟节点是黑色,并且X是父节点的左子节点。操作步骤如下:

  • 将父节点染黑 setColor(parentOf(x), BLACK);
  • 将父节点的父节点染红 setColor(parentOf(parentOf(x)), RED);
  • 以父节点做右旋转 rotateRight(parentOf(parentOf(x)));
    在这里插入图片描述
    抽出来的代码如下:
private void fixAfterInsertion(Entry<K,V> x) {
	//...省略代码
	if (x == rightOf(parentOf(x))) {//case 2,把case 2转化成 case 3
	   x = parentOf(x);
	   rotateLeft(x);
	}
	setColor(parentOf(x), BLACK);	//case 3
    setColor(parentOf(parentOf(x)), RED);
    rotateRight(parentOf(parentOf(x)));
    //...省略代码
}

注:上述三个case是在X的父节点是祖父节点的左子树条件下的,如果是右子树,结论一样,总的代码:

    private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED;//节点创建时是黑的(因为默认创建的是叶子节点),如果确认会发生插入操作,将其染红

        while (x != null && x != root && x.parent.color == RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//当前父节点是祖父节点的左子树
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {	//case 1
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == rightOf(parentOf(x))) {	//case 2
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);	//case 3
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {//当前父节点是祖父节点的右子树
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));//只在此处有差异
                if (colorOf(y) == RED) {	//case 1
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {	//case 2
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);	//case 3
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }
3.“删”类方法

1)、remove
与put方法类似,remove方法也可以分为两个步骤进行:
第一步:将红黑树当作一颗二叉查找树,将节点删除。这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:

  • 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
  • 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
  • 被删除节点有两个儿子。那么按一下顺序
    1)先找出它的后继节点;
    2)然后把“它的后继节点的内容”复制给“该节点的内容”;
    3)之后,删除“它的后继节点”。
    在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。

第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
整体代码如下:

    public V remove(Object key) {
        Entry<K,V> p = getEntry(key);	//containKey方法中有介绍
        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;
        } // p has 2 children

        // Start fixup at replacement node, if it exists.
        //只有单孩子节点(双孩子节点的P已经到达叶子节点,此时的p无孩子,所以不会再次处理双孩子的节点了)
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);

        if (replacement != null) {//p有一个孩子,用p的孩子替代自己的位置
            // Link replacement to parent
            replacement.parent = p.parent;//把p的孩子的parent指向p的parent,即删掉p
            if (p.parent == null)//删除的p是根节点了,则用p的孩子做根
                root = replacement;
            else if (p == p.parent.left)
            	//p不是根节点时,若p是左节点,则把p->parent.left指向p的孩子
                p.parent.left  = replacement;
            else
            	//p不是根节点时,若p是右节点,则把p->parent.right指向p的孩子
                p.parent.right = replacement;

            // Null out links so they are OK to use by fixAfterDeletion.
            p.left = p.right = p.parent = null;

            // 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.
        	//要删除的是叶子节点,或者要删除的节点p已经和后继节点置换了,直接删除它
            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;
            }
        }
    }

再平衡fixAfterDeletion
假设要删除的节点为x,替换他的节点是y(x有双孩子,y是x的后继节点;x有单孩子时,y是x的孩子;x没有孩子,y一个黑色叶子NIL)。则有四种情况需要进行再平衡:
case 1:x是黑的,y是黑的(或者y是叶子节点),并且x的兄弟节点是红的,调整步骤如下:

  • 把x的兄弟节点染黑 setColor(sib, BLACK);
  • 把x的父节点染红 setColor(parentOf(x), RED);
  • 以x的父节点做左旋转 rotateLeft(parentOf(x));
  • 重新设置x的兄弟节点 sib = rightOf(parentOf(x));
    在这里插入图片描述
    相应的剥离代码块:
private void fixAfterDeletion(Entry<K,V> x) {
	while (x != root && colorOf(x) == BLACK) {
		//...省略代码
		if (colorOf(sib) == RED) {//case 1
        	setColor(sib, BLACK);
            setColor(parentOf(x), RED);
            rotateLeft(parentOf(x));
            sib = rightOf(parentOf(x));
        }
        //...省略代码
	}
}

case 2:x是黑的,y也是黑的;并且x的兄弟节点是黑的,并且兄弟节点的两个孩子也是黑的

  • 将x的兄弟节点设为“红色”。
  • 设置“x的父节点”为“新的x节点”。
    在这里插入图片描述
    相应的剥离代码块:
private void fixAfterDeletion(Entry<K,V> x) {
	while (x != root && colorOf(x) == BLACK) {
		//...省略代码
		 if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) {
             setColor(sib, RED);
             x = parentOf(x);
         }
        //...省略代码
	}
}

case 3:x是黑的,y也是黑的;并且x的兄弟节点是黑的,并且兄弟节点的左孩子是红,右孩子为黑

  • 将x兄弟节点的左孩子设为“黑色”。 setColor(leftOf(sib), BLACK);
  • 将x兄弟节点设为“红色”。 setColor(sib, RED);
  • 对x的兄弟节点进行右旋。 rotateRight(sib);
  • 右旋后,重新设置x的兄弟节点。 sib = rightOf(parentOf(x));
    最终转化为case 4
    在这里插入图片描述
    与case 4一起剥离代码。

case 4:x是黑色的,y也是黑的;x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色

  • 将x父节点颜色 赋值给 x的兄弟节点。 setColor(sib, colorOf(parentOf(x)));
  • 将x父节点设为“黑色”。 setColor(parentOf(x), BLACK);
  • 将x兄弟节点的右子节设为“黑色”。 setColor(rightOf(sib), BLACK);
  • 对x的父节点进行左旋。 rotateLeft(parentOf(x));
  • 设置“x”为“根节点”,把25号节点删掉,红黑树再次平衡。 x = root;
    在这里插入图片描述
    相应的剥离代码块:
private void fixAfterDeletion(Entry<K,V> x) {
	while (x != root && colorOf(x) == BLACK) {
		//...省略代码
        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;
        //...省略代码
	}
}

完整的fixAfterDeletion如下:

   private void fixAfterDeletion(Entry<K,V> x) {
        while (x != root && colorOf(x) == BLACK) {//遇到红节点,调整结束
            if (x == leftOf(parentOf(x))) {//x是左子树,(x是右子树时是一样的)
                Entry<K,V> sib = rightOf(parentOf(x));

                if (colorOf(sib) == RED) {//case 1:兄弟节点是红的
                    setColor(sib, BLACK);
                    setColor(parentOf(x), RED);
                    rotateLeft(parentOf(x));
                    sib = rightOf(parentOf(x));
                }

                if (colorOf(leftOf(sib))  == BLACK &&
                    colorOf(rightOf(sib)) == BLACK) {//case 2:兄弟节点两个孩子都是黑色的
                    setColor(sib, RED);
                    x = parentOf(x);
                } else {
                    if (colorOf(rightOf(sib)) == BLACK) {//case 3:兄弟节点左孩子红,右孩子黑
                    									 //转化成case 4
                        setColor(leftOf(sib), BLACK);
                        setColor(sib, RED);
                        rotateRight(sib);
                        sib = rightOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));//case 4:兄弟节点右孩子红,左孩子任意
                    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);
    }

2)、clear
clear很简单,把跟置空空,就会逐层瓦解整棵树

    public void clear() {
        modCount++;
        size = 0;
        root = null;
    }
4.“改”类方法

改类方法(改变V)不会使得红黑树失去平衡(是依据key排序的),所以较简单。有两个方法
V replace(K key, V value):用value替换当前K处的old value;如果old value为空,不能替换。
boolean replace(K key, V oldValue, V newValue):只有树中old value==传入参数的oldvalue时才进行替换,否则返回false

    public V replace(K key, V value) {
        Entry<K,V> p = getEntry(key);
        if (p!=null) {
            V oldValue = p.value;
            p.value = value;
            return oldValue;
        }
        return null;
    }
    public boolean replace(K key, V oldValue, V newValue) {
        Entry<K,V> p = getEntry(key);
        if (p!=null && Objects.equals(oldValue, p.value)) {
            p.value = newValue;
            return true;
        }
        return false;
    }
5.“查”类方法

get类方法不会影响树的平衡,所以与一般的容器取值没有太大差别。
分为获取key,获取value,和获取entry<k,v>
1)V get(Object key)
直接调用的getEntry

    public V get(Object key) {
        Entry<K,V> p = getEntry(key);
        return (p==null ? null : p.value);
    }

2)获取key的方法:都比较简单
public K firstKey():获得最小的key
public K lastKey():获得最大的key
public K lowerKey(K key):获得比参数key上一个小的k
public K higherKey(K key):获得比参数key下一个大的k
public K ceilingKey(K key):获得比该键至少大于或等于的k

    public K firstKey() {
        return key(getFirstEntry());一直往左子树里找
    }
    public K lastKey() {
        return key(getLastEntry());//一直往右子树里找
    }
    public K lowerKey(K key) {
        return keyOrNull(getLowerEntry(key));
    }
    public K higherKey(K key) {
        return keyOrNull(getHigherEntry(key));
    }
    public final K ceilingKey(K key) {
        return keyOrNull(getCeilingEntry(key));
    }

3)获取entry的方法
public Map.Entry<K,V> firstEntry() :获得K最小的那个entry
public Map.Entry<K,V> lastEntry() :获得K最大的那个entry
public Map.Entry<K,V> pollFirstEntry():获得K最小的那个entry,并把它移除出树
public Map.Entry<K,V> pollLastEntry():获得K最大的那个entry,并把它移除出树
public Map.Entry<K,V> lowerEntry(K key):获得比K小一个的那个entry
public Map.Entry<K,V> ceilingEntry(K key):获得比该K至少大于或等于的entry
public Map.Entry<K,V> higherEntry(K key):获得比K大一个的那个entry
public Map.Entry<K,V> floorEntry(K key):获得比K小一个的那个entry

    public Map.Entry<K,V> firstEntry() {
        return exportEntry(getFirstEntry());
    }
    public Map.Entry<K,V> lastEntry() {
        return exportEntry(getLastEntry());
    }
    public Map.Entry<K,V> pollFirstEntry() {
        Entry<K,V> p = getFirstEntry();
        Map.Entry<K,V> result = exportEntry(p);
        if (p != null)
            deleteEntry(p);
        return result;
    }
    public Map.Entry<K,V> pollLastEntry() {
        Entry<K,V> p = getLastEntry();
        Map.Entry<K,V> result = exportEntry(p);
        if (p != null)
            deleteEntry(p);
        return result;
    }
    public Map.Entry<K,V> higherEntry(K key) {
        return exportEntry(getHigherEntry(key));
    }
    public Map.Entry<K,V> lowerEntry(K key) {
        return exportEntry(getLowerEntry(key));
    }
    public Map.Entry<K,V> ceilingEntry(K key) {
        return exportEntry(getCeilingEntry(key));
    }
    public Map.Entry<K,V> floorEntry(K key) {
        return exportEntry(getFloorEntry(key));
    }
6.函数式方法

1) forEach(BiConsumer<? super K, ? super V> action)
二元函数,使用方法:treeMapB.forEach((k,v)->System.out.println(" key = " + k.getId() + ", value = " + v));
在HashMap中详尽的解读过,除了遍历的for语句有差别,其他都一样

    public void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        int expectedModCount = modCount;
        for (Entry<K, V> e = getFirstEntry(); e != null; e = successor(e)) {
            action.accept(e.key, e.value);
            if (expectedModCount != modCount) {
                throw new ConcurrentModificationException();
            }
        }
    }

2) replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
二元函数式,使用方法treeMapB.replaceAll((k,v)->(int)k.getAccount()+v);//把每个key的Account值加目前的value,替换 oldvalue
遍历所有节点,处理function.apply(e.key, e.value);把新值进行赋值

    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        int expectedModCount = modCount;
        for (Entry<K, V> e = getFirstEntry(); e != null; e = successor(e)) {
            e.value = function.apply(e.key, e.value);
            if (expectedModCount != modCount) {
                throw new ConcurrentModificationException();
            }
        }
    }
六、重要内部类

分为四种:
1.内部容器类:KeySet,Values,EntrySet。注意这三个有区别

  • KeySet是有序的set(实现NavigableSet接口)
  • Values和EntrySet是无序的,Values是AbstractCollection,EntrySet是AbstractSet。这是因为Values可以有重复的值,所以普通容器就好;但是EntrySet必须不能重复

2.迭代器类:EntryIterator,ValueIterator,KeyIterator均继承于PrivateEntryIterator,具体实现均在后者。另外红黑树还提供了DescendingKeyIterator,倒序的key迭代器,具体实现也是在PrivateEntryIterator中

3.子map以及子map的迭代器类:SubMap,AscendingSubMap(正序),DescendingSubMap(反序)。对应的迭代器:SubMapEntryIterator,DescendingSubMapEntryIterator,SubMapKeyIterator,DescendingSubMapKeyIterator
4.并行遍历迭代器:TreeMapSpliterator,KeySpliterator,DescendingKeySpliterator,ValueSpliterator,EntrySpliterator
每一种类型的实现都比较相似,择一来详读

1、内部容器类

遍历时,经常要用到EntrySet例如:for(Map.Entry<K,V> e: map.entrySet()),此时便会调用EntrySet

    public Set<Map.Entry<K,V>> entrySet() {
        EntrySet es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
    }

它有如下方法,只能删除entry,不能插入entry

    class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator(getFirstEntry());
        }

        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
            Object value = entry.getValue();
            Entry<K,V> p = getEntry(entry.getKey());
            return p != null && valEquals(p.getValue(), value);
        }

        public boolean remove(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
            Object value = entry.getValue();
            Entry<K,V> p = getEntry(entry.getKey());
            if (p != null && valEquals(p.getValue(), value)) {
                deleteEntry(p);
                return true;
            }
            return false;
        }

        public int size() {
            return TreeMap.this.size();
        }

        public void clear() {
            TreeMap.this.clear();
        }

        public Spliterator<Map.Entry<K,V>> spliterator() {
            return new EntrySpliterator<K,V>(TreeMap.this, null, null, 0, -1, 0);
        }
    }
2、迭代器类

倒序key遍历器DescendingKeyIterator。提供向前遍历方法prevEntry(),向后遍历nextEntry()
倒序的next=正序的prev

    final class DescendingKeyIterator extends PrivateEntryIterator<K> {
        DescendingKeyIterator(Entry<K,V> first) {
            super(first);
        }
        public K next() {
            return prevEntry().key;
        }
        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            deleteEntry(lastReturned);
            lastReturned = null;
            expectedModCount = modCount;
        }
    }
3、子map以及子map的迭代器类

切分有有三种方式,自定义切,切头,切尾,最终都会新建一个AscendingSubMap类

public SortedMap<K,V> subMap(K fromKey, K toKey)public SortedMap<K,V> headMap(K toKey)
public SortedMap<K,V> tailMap(K fromKey)
//例如:
public SortedMap<K,V> tailMap(K fromKey) {
    return tailMap(fromKey, true);
}
public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) {
    return new AscendingSubMap<>(this,
                                 false, fromKey, inclusive,
                                 true,  null,    true);
}

submap的迭代器的结构与TreeMap的迭代器结构一样。没有与主map共用一套迭代器,我想是为了更模块化,以及减少错误吧。subMap是浅拷贝,对subMap的改动也会影响到TreeMap。

4、并行遍历器类

TreeMap提供5种并行遍历器:

  • TreeMapSpliterator:没找到入口在哪?
  • KeySpliterator:对Key的遍历器–>使用入口在KeySet.spliterator()
  • DescendingKeySpliterator:没找到入口在哪?
  • ValueSpliterator :对值的遍历器–>使用入口在Values.spliterator()
  • EntrySpliterator:对entry的遍历器–>使用入口在EntrySet.spliterator()
    提供的主要方法,和HashMap中的基本一样,以entry为例
public EntrySpliterator<K,V> trySplit()
public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action)
public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action)

多线程时,每个线程切一小段trySplit(),然后处理forEachRemaining,从而达到并发的效果

七、测试类

测试用用,用于做key,分为实现Comparable接口的UserA,和不实现的UserB

public class UserA implements Comparable<UserA>{
	int id;
	String name;
	long account;
	public UserA(int id, String name, long account) {
		this.id = id;
		this.name = name;
		this.account = account;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public long getAccount() {
		return account;
	}
	public void setAccount(long account) {
		this.account = account;
	}

	@Override
	public int compareTo(UserA o) {
		// TODO Auto-generated method stub		
		return (this.getId()<o.getId())?-1:1;
	}
}

不实现Comparable接口的UserB

public class UserB {
	int id;
	String name;
	long account;
	public UserB(int id, String name, long account) {
		this.id = id;
		this.name = name;
		this.account = account;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public long getAccount() {
		return account;
	}
	public void setAccount(long account) {
		this.account = account;
	}
}

测试用main

	public static void main(String[] args) {
		//测试组1:使用默认方法创建TreeMap,按Id正序建立
		System.out.println("测试组1:使用内部比较器comparable创建TreeMap");
		TreeMap< UserA, Integer> treeMapA = new TreeMap<UserA, Integer>();
		UserA a = new UserA(1, "userA 1",100);
		treeMapA.put(new UserA(1, "userA 1",100), 1);
		treeMapA.put(new UserA(3, "userA 3",300), 3);
		treeMapA.put(new UserA(2, "userA 2",200), 2);	
		treeMapA.forEach((k,v)->System.out.println("    key = " + k.getId() + ", value = " + v));
		
		treeMapA = null;//干掉map
		
		//测试组2:使用自定义Comparator方法创建TreeMap,按Id正序建立
		System.out.println("测试组2:使用外部比较器Compartor创建TreeMap");
		TreeMap< UserB, Integer> treeMapB = new TreeMap<UserB, Integer>((o1,o2)->(o1.getId()<o2.getId())?-1:1);
		treeMapB.put(new UserB(1, "userB 1",100), 1);
		treeMapB.put(new UserB(3, "userB 3",300), 3);
		treeMapB.put(new UserB(2, "userB 2",200), 2);	
		treeMapB.forEach((k,v)->System.out.println("    key = " + k.getId() + ", value = " + v));
		
		//测试组3:测试lowerEntry,并对其进行修改,可以看到影响到原map
		System.out.println("测试组3:测试lowerEntry,并对其进行修改,可以看到影响到原map");
		Map.Entry<UserB, Integer> b = treeMapB.lowerEntry(new UserB(5, "userB 1",100));
		b.getKey().setId(4);
		treeMapB.forEach((k,v)->System.out.println("    key = " + k.getId() + ", value = " + v));
		b.getKey().setId(3);
		
		//测试组4:测试replaceAll
		System.out.println("测试组4:测试firstKey,并对其进行修改,可以看到不影响到原map");
		treeMapB.replaceAll((k,v)->(int)k.getAccount()+v);
		treeMapB.forEach((k,v)->System.out.println("    key = " + k.getId() + ", value = " + v));
		
		//测试组5:测试subMap
		System.out.println("测试组5:测试subMap");
		SortedMap< UserB, Integer> treeMapC = treeMapB.subMap(new UserB(1, "userB 1",100), new UserB(3, "userB 1",100));
		System.out.println("分割后的subMap");
		treeMapC.forEach((k,v)->System.out.println("    key = " + k.getId() + ", value = " + v));
		System.out.println("分割后的主Map");
		treeMapB.forEach((k,v)->System.out.println("    key = " + k.getId() + ", value = " + v));
		
		//测试组6:测试Spliterator
		System.out.println("测试组6:测试Spliterator");
		Set<Map.Entry<UserB, Integer>> entry = treeMapB.entrySet();
		Spliterator<Entry<UserB, Integer>> sit = entry.spliterator();
		//切割一次,遍历切掉的部分:
		System.out.println("切割一次,遍历切剩下的部分:");
		sit.trySplit().forEachRemaining(e->System.out.println("    key = " + e.getKey().getId()));
		//遍历剩下的部分:
		System.out.println("遍历剩下的部分:");
		sit.forEachRemaining(e->System.out.println("    key = " + e.getKey().getId()));			
	}
八、总结
  1. TreeMap是有序的,实现的原理是Key自然有序(int,long等),或实现Comparable接口,或者提供外部比较器
  2. TreeMap是实现TreeSet的基础
  3. 左旋,右旋;put,remove操作
  4. 待补充
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值