zoukankan      html  css  js  c++  java
  • java.util.TreeMap源码分析

    TreeMap的实现基于红黑树,排列的顺序根据key的大小,或者在创建时提供的比较器,取决于使用哪个构造器。

    对于,containsKey,get,put,remove操作,保证时间复杂度为log(n)。

    TreeMap的顺序与equals方法保持一致,这样才能遵守Map和SortMap的约定。

    实现非同步,运行在多线程环境下,要进行外部同步,或者调用SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));方法得到一个同步化的TreeMap。

    迭代器是快速失败的。

    //AbstractMap实现Map接口,NavigableMap实现SortedMap接口,为了遵守约定,key的equals方法和客户提供的比较器或者key本身的compare结果要一致
    public class TreeMap<K,V>
        extends AbstractMap<K,V>
        implements NavigableMap<K,V>, Cloneable, java.io.Serializable

    实例变量:

    //客户提供的比较器,如果不提供,为null
    private final Comparator<? super K> comparator;
    
    //TreeMap的根结点
    private transient Entry<K,V> root = null;
    
    //TreeMap含有的结点数
    private transient int size = 0;
    
    //TreeMap的修改次数,用于迭代器快速失败机制
    private transient int modCount = 0;

    TreeMap的红黑树结点实现

    //结点颜色
    private static final boolean RED   = false;
    private static final boolean BLACK = true;
    
    //TreeMap的红黑树结点实现
    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;//颜色,初始化时为黑色
    
            /**
             * Make a new cell with given key, value, and parent, and with
             * {@code null} child links, and BLACK color.
             */
            Entry(K key, V value, Entry<K,V> parent) {
                this.key = key;
                this.value = value;
                this.parent = parent;
            }
    
            /**
             * Returns the key.
             *
             * @return the key
             */
            public K getKey() {
                return key;
            }
    
            /**
             * Returns the value associated with the key.
             *
             * @return the value associated with the key
             */
            public V getValue() {
                return value;
            }
    
            /**
             * Replaces the value currently associated with the key with the given
             * value.
             *
             * @return the value associated with the key before this method was
             *         called
             */
            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;
            }
    }

    4个构造器

        //不提供比较器,使用key的自然顺序
        public TreeMap() {
            comparator = null;
        }
        
        //提供比较器
        public TreeMap(Comparator<? super K> comparator) {
            this.comparator = comparator;
        }
    
        //使用m构造TreeMap,m没有比较器,所以还是使用key的自然顺序
        public TreeMap(Map<? extends K, ? extends V> m) {
            comparator = null;
            putAll(m);
        }
        
        //使用m构造TreeMap,m可能有,也可能没有比较器
        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) {
            }
        }

    put操作:

        public V put(K key, V value) {
            Entry<K,V> t = root;
            //如果根节点为null
            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;
            // split comparator and comparable paths
            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 {//没有提供比较器,使用key的自然顺序,思想与上面一样
                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);
                } 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;
        }

    插入后调整

        private void fixAfterInsertion(Entry<K,V> x) {
            x.color = RED;//节点颜色设置为红色
            //循环条件x不为空,且不是根节点且父节点颜色是红色,如果不是红色,那就没有连续的红色节点,不需要调整,如果x是根节点,不需要调整,设置为黑色即可
            while (x != null && x != root && x.parent.color == RED) {
                //如果x的父节点是x的父节点的父节点的左节点
                if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                    //取x的父节点的父节点的右节点
                    Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                    //如果y的颜色是红色
                    if (colorOf(y) == RED) {
                        //将x的父节点设置为黑色
                        setColor(parentOf(x), BLACK);
                        //将y的颜色设置为黑色
                        setColor(y, BLACK);
                        //将x的父结点的父节点设置为红色
                        setColor(parentOf(parentOf(x)), RED);
                        //x的父节点的父节点的性质可能被破坏,递归地处理这个过程
                        x = parentOf(parentOf(x));
                    } else {
                        //如果x是父节点的右节点
                        if (x == rightOf(parentOf(x))) {
                            x = parentOf(x);
                            //左旋
                            rotateLeft(x);
                        }
                        setColor(parentOf(x), BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        rotateRight(parentOf(parentOf(x)));
                    }
                } else {//如果x的父节点是x父节点的父节点的右节点
                    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;//根节点始终为黑色
        }        

    remove操作

        public V remove(Object key) {
            //找出指定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 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.
            Entry<K,V> replacement = (p.left != null ? p.left : p.right);
    
            if (replacement != null) {
                // Link replacement to parent
                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;
    
                // 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.
                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;
                }
            }
        }

    get操作

        public V get(Object key) {
            Entry<K,V> p = getEntry(key);
            return (p==null ? null : p.value);
        }
    
            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;//找不到,返回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)
                        p = p.left;
                    else if (cmp > 0)
                        p = p.right;
                    else
                        return p;
                }
            }
            return null;
        }

    红黑数满足的5个性质:

    性质 1:每个节点要么是红色,要么是黑色。

    性质 2:根节点永远是黑色的。

    性质 3:所有的叶节点都是空节点(即 null),并且是黑色的。

    性质 4:每个红色节点的两个子节点都是黑色。(从每个叶子到根的路径上不会有两个连续的红色节点)

    性质 5:从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。

    插入或删除节点之后调整的目的都是为了满足上面的5个性质。

  • 相关阅读:
    读写文件流
    关闭和退出窗口
    有什么问题?
    将aspx页面转换成htm页面
    读取rss聚合文件
    运算符重载实例
    委托
    将 Visual Studio .NET 调试器用于 ASP.NET 应用程序
    输入的字符串进行有规则的清洗
    几个常用的数据库连接字符串
  • 原文地址:https://www.cnblogs.com/13jhzeng/p/5793972.html
Copyright © 2011-2022 走看看