zoukankan      html  css  js  c++  java
  • TreeMap源码解析

    public class TreeMap<K,V>
        extends AbstractMap<K,V>
        implements NavigableMap<K,V>, Cloneable, java.io.Serializable
    

    HashMap继承抽象类AbstractMap,实现了NavigableMap接口,NavigableMap继承自SortedMap,方法有lowerEntry小于,floorEntry小于等于,ceilingEntry大于,higherEntry大于等于,便于搜索查找。

    • TreeMap的属性
        private final Comparator<? super K> comparator;                 //保存顺序的比较器,
        private transient Entry<K,V> root = null;                       //根节点
        private transient int size = 0;                                 //树中的节点数量
        private transient int modCount = 0;
        private static final boolean RED   = false;
        private static final boolean BLACK = true;
    
    • TreeMap的构造方法
        public TreeMap() {
            comparator = null;                                          //comparator为空,即采用自然顺序维持TreeMap中节点的顺序
        }
        public TreeMap(Comparator<? super K> comparator) {              //带有比较器的构造方法
            this.comparator = comparator;
        }
        public TreeMap(Map<? extends K, ? extends V> m) {
            comparator = null;
            putAll(m);                                                  //调用putAll方法将Map中的所有元素加入到TreeMap中
        }
        public TreeMap(SortedMap<K, ? extends V> m) {                   //根据SortedMap的比较器维持TreeMap中的节点顺序
            comparator = m.comparator();
            try {
                buildFromSorted(m.size(), m.entrySet().iterator(), null, null);         //SortedMap中的内容添加到TreeMap中
            } catch (java.io.IOException cannotHappen) {
            } catch (ClassNotFoundException cannotHappen) {
            }
        }
    
    • 静态内部类
        static final class Entry<K,V> implements Map.Entry<K,V> {
            K key;                                              //键
            V value;                                            //值
            Entry<K,V> left = null;                             //左节点
            Entry<K,V> right = null;                            //右节点
            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;
            }
            public K getKey() {
                return key;
            }
            public V getValue() {
                return value;
            }
            public V setValue(V value) {
                V oldValue = this.value;
                this.value = value;
                return oldValue;
            }
            public boolean equals(Object o) {
                if (!(o instanceof Map.Entry))
                    return false;
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
    
                return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
            }
            public int hashCode() {
                int keyHash = (key==null ? 0 : key.hashCode());
                int valueHash = (value==null ? 0 : value.hashCode());
                return keyHash ^ valueHash;
            }
            public String toString() {
                return key + "=" + value;
            }
        }
    

    put

        public V put(K key, V value) {
            Entry<K,V> t = root;
            if (t == null) {                                //根节点为null,则直接插入根节点
                compare(key, key);
                root = new Entry<>(key, value, null);       //创建个新的entry节点,指向root
                size = 1;                                   //当前容量是1
                modCount++;
                return null;
            }
            int cmp;                                         //cmp表示key排序的返回结果
            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的key,则当做左子节点 
                parent.left = e;
            else
                parent.right = e;                                               //大于parent的key,则当做右子节点 
            fixAfterInsertion(e);                                               //恢复红黑树的特性
            size++;
            modCount++;
            return null;
        }
        final int compare(Object k1, Object k2) {
            return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
                : comparator.compare((K)k1, (K)k2);
        }
         /** From CLR */
        private void fixAfterInsertion(Entry<K,V> x) {
            x.color = RED;
    
            while (x != null && x != root && x.parent.color == RED) {   //循环 节点不为null且不为根节点且节点的父节点是红色
                if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {     //当前节点的父节点是当前节点的爷爷节点的的左节点(叔叔节点)
                    Entry<K,V> y = rightOf(parentOf(parentOf(x)));      //获取当前节点的叔叔节点(爷爷有且只有2个孩子,左是父节点,右是叔节点)
                    if (colorOf(y) == RED) {                            //假如叔叔节点是红色
                        setColor(parentOf(x), BLACK);                   //设置父节点是黑色
                        setColor(y, BLACK);                             //设置叔叔节点是黑色
                        setColor(parentOf(parentOf(x)), RED);           //设置爷爷节点是红色
                        x = parentOf(parentOf(x));                      //将爷爷节点指向当前节点
                    } else {                                            //当前节点的父节点是当前节点的爷爷节点的的右节点(叔叔节点)
                        if (x == rightOf(parentOf(x))) {                //当前节点是其父节点的右节点
                            x = parentOf(x);                            //父节点指向当前节点
                            rotateLeft(x);                              //左旋
                        }
                        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;                                         //根节点设置为黑色
        }
    
    
        private void rotateLeft(Entry<K,V> p) {                         //左旋
            if (p != null) {
                Entry<K,V> r = p.right;                                 
                p.right = r.left;                                       
                if (r.left != null)                                     
                    r.left.parent = p;                                  
                r.parent = p.parent;
                if (p.parent == null)
                    root = r;
                else if (p.parent.left == p)
                    p.parent.left = r;
                else
                    p.parent.right = r;
                r.left = p;
                p.parent = r;
            }
        }
        private void rotateRight(Entry<K,V> p) {                        //右旋
            if (p != null) {
                Entry<K,V> l = p.left;
                p.left = l.right;
                if (l.right != null) l.right.parent = p;
                l.parent = p.parent;
                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;
            }
        }
    
    添加一组数据来讲解吧,注释写不下去了-_-! 
    

    eg:

            TreeMap m = new TreeMap();
        	m.put(2,1);
        	m.put(3,2);
        	m.put(5,3);
        	m.put(6,4);
        	m.put(8,5);
        	m.put(9,6);
        	m.put(10,7);
            m.put(1,8);
    
    按顺序添加,先添加key为2的数据,put方法,root为null,创建一个节点,节点的color属性默认是黑色,这次2是根节点,如下图
    

    image

    添加key为3后,如下图
    

    image
    默认的比较器,比较key,3比2大,所以放到右边。fixAfterInsertion后,将key设置为红色,root节点设置为黑色。

    添加key为5后,如下图
    

    image
    比较key,5比2大,所以放到右边,比3也大,就放3右边了,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x))=null,parentOf(x)是key为3的节点,
    进入到else判断,leftOf(parentOf(parentOf(x)))=null,colorOf(y)=BLACK进入else判断,leftOf(parentOf(x))=null,不会进入到if判断,将当前节点5的父
    节点3设置为黑色,将爷爷节点2设置为红色,进行左旋操作,左旋是将根节点2作为参数,传进去的,左旋后,3变成了根节点,2变成了3的左子节点。

    添加key为6后,如下图
    

    image
    比较key,6比3大,所以放到右边,比5也大,就放5右边了,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x))=null,parentOf(x)是key为5的节点,
    进入到else判断,leftOf(parentOf(parentOf(x)))是节点2,color是红色的,colorOf(y) == RED进入if判断,将当前节点6的父节点5设置为黑色,将节点2设置为黑色,
    将当前节点6的爷爷节点3设置为红色,将爷爷节点指向3,跳出循环,设置根节点为黑色。

    添加key为8后,如下图
    

    image
    比较key,8比3大,放到右边,比5也大,放右边,比6也大,放6右边,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x))=null,
    parentOf(x)是key为6的节点,进入到else判断,leftOf(parentOf(parentOf(x)))是节点2,color是黑色的,进入else判断,leftOf(parentOf(x)为null,
    不等于当前x节点,将当前节点8的父节点6设置为黑色,将爷爷节点5设置为红色,进行左旋操作,左旋是将爷爷节点5作为参数,传进去的,左旋后,5变成了6的左子节点,6变成3的右节点。

    添加key为9后,如下图
    

    image
    比较key,9比3大,放到右边,比6大,放右边,比8大,放右边,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x))是key为5的节点,parentOf(x)是key为8的节点,进入到else判断,leftOf(parentOf(parentOf(x)))是节点5,color是红色,进入if判断,将当前x节点的父节点8设置为黑色,将节点5设置为黑色,将当前节点的爷爷节点
    6设置为红色,将爷爷节点6指向当前插入节点9,此时x节点是节点6了,x.parent.color是黑色,所以跳出循环,设置根节点为黑色。

    添加key为10后,如下图
    

    image
    比较key,10比3大,放到右边,比6大,放右边,比8大,放右边,比9大,放右边,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x)),key8的左子节点是空,parentOf(x)是key为9的节点,进入到else判断,leftOf(parentOf(parentOf(x)))是空,进入else判断,leftOf(parentOf(x))是空,不等于当前节点x,设置当前节点10的父节点9为黑色,当前节点10的爷爷节点8为红色,进行左旋操作,左旋是将爷爷节点8作为参数,传进去的,左旋后,8变成了9的左子节点,9变成6的右节点。

    添加key为1后,如下图
    

    image
    比较key,1比3小,放到左边,比2小,放左边,fixAfterInsertion后,x.parent.color,x.parent是节点2,是黑色的,所以没进入循环,设置根节点是黑色就结束了。

    总结下特点:
    1.根节点总是黑色。
    2.新插入的节点是默认是黑色,在经过fixAfterInsertion方法后,会变成红色。
    3.fixAfterInsertion方法:当前节点不是空,不是根节点,父节点是红色才会进行调整操作。(一个路径上不能有2个红色相邻的节点)
    4.根节点到所有叶子节点的数量必须相同(上例put方法,key为5,为了保持平衡进行左旋操作)
    

    remove

        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) {        //被删除节点的左子节点和右子节点都不为空,则用p节点的中序后继节点代替p节点 
                Entry<K,V> s = successor(p);                //寻找P的替代节点
                p.key = s.key;
                p.value = s.value;
                p = s;
            } // p has 2 children
    
            Entry<K,V> replacement = (p.left != null ? p.left : p.right);       //替代节点,左子节点不能空则优先使用左子节点,否则就用右子节点
    
            if (replacement != null) {                                          //替代节点不为空
                replacement.parent = p.parent;          
                if (p.parent == null)                                          
                    root = replacement;                                          //p节点没有父节点,替代节点作根节点
                else if (p == p.parent.left)
                    p.parent.left  = replacement;                               //P为左节点,则用replacement来替代为左节点 
                else
                    p.parent.right = replacement;                               //P为右节点,则用replacement来替代为右节点  
    
                p.left = p.right = p.parent = null;                             //P节点从这棵树中剔除掉
    
                if (p.color == BLACK)                       
                    fixAfterDeletion(replacement);                              //P为黑色,则需要调整红黑树
            } else if (p.parent == null) { // return if we are the only node.
                root = null;                                                    //P为根节点,直接删除 
            } else {                                                            //P节点不存在子节点,直接删除
                if (p.color == BLACK)
                    fixAfterDeletion(p);                                        //P节点的颜色为黑色,调整红黑树
    
                if (p.parent != null) {                                         //删除P节点
                    if (p == p.parent.left)
                        p.parent.left = null;
                    else if (p == p.parent.right)
                        p.parent.right = null;
                    p.parent = null;
                }
            }
        }
        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;
            }
        }
        /** From CLR */
        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
                    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);
        }
    

    eg:

            TreeMap m = new TreeMap();
        	m.put(2,1);
        	m.put(3,2);
        	m.put(5,3);
        	m.put(6,4);
        	m.put(8,5);
        	m.put(9,6);
        	m.put(10,7);
            m.put(1,8);
            m.remove(3);
            m.remove(9);
            m.remove(1);
    
    删除根节点3,如下图
    

    image
    判断节点3,有没有左子节点和右子节点,优先寻找右子节点的最左子节点,没有右子节点,则寻找寻找左子节点的最右子节点,节点3的右子节点的最左节点是5,节点5作为替换节点,没有左子节点和右子节点,节点5是黑色,进行调整操作,调用fixAfterDeletion方法,调整红黑树结构。

    删除节点9,如下图
    

    image
    节点9没有右节点的最左子节点,那么就取左节点的最右子节点了,节点8刚好是黑色的,进行调整操作,调用fixAfterDeletion方法,调整红黑树结构。

      删除节点1,如下图
    

    image
    节点1没有左右子节点,且刚好是红色的,直接删除了。

    总结下删除的3种情况:

    1. 待删除节点没有子节点,直接删掉。
    2. 待删除节点只有一个子节点,直接删除。
      1. 待删除的子节点是黑色,子节点修改成黑色。
      2. 待删除的子节点是红色,直接删除。
    3. 待删除节点有2个子节点
      1. 找出替换节点,优先寻找待删除节点的右子节点的最左子节点,找不到的话,就左子节点的最右子节点。
      2. 调整待删除节点的父节点和替换节点的关系。
      3. 调整替换节点的子节点和被删除节点子节点的关系。

    总结

    TreeMap由红黑树结构来实现的,以key-value存在。key不允许为空,value允许为空,key重复会覆盖,value可以重复,有序,非线程安全。

  • 相关阅读:
    6 完全平方数相关
    5 三位数,每个位置不同
    Neo4j Admin Import 导入多个node和relationship
    Rust所有权
    Rust 多态
    Rust 泛型
    Rust trait
    Rust模块化
    Spring Cloud(Dalston.SR1)
    git 速度慢问题解决
  • 原文地址:https://www.cnblogs.com/Ch1nYK/p/8832322.html
Copyright © 2011-2022 走看看