TreeMap源码分析

说明:基于jdk1.7源码

TreeMap是基于红黑树实现的,关于红黑树的介绍可以参考:排序二叉树、平衡二叉树和红黑树

一、概述

Map接口的实现有HashMap、LinkedHashMap、TreeMap等。
HashMap不保证数据有序
LinkedHashMap保证数据可以保持插入顺序
而如果希望Map可以保持key的大小顺序的时候,我们就需要利用TreeMap了。

二、源码分析

1.节点的定义

每个节点由K,V,左右子节点,父节点组成,并且初始颜色为黑色

    /**
     * Node in the Tree.  Doubles as a means to pass key-value pairs back to
     * user (see Map.Entry).  
     * 树中节点的定义。每个节点:[<键,值>,左节点,右节点,父节点,是否黑色]
     */
    static final class Entry<K,V> implements Map.Entry<K,V> {
        //K
        K key;
        //V
        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.
         * 使用指定的key,value和所属的父节点来创建一个节点,颜色为黑色。其子节点为空。
         */
        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;
        }

        /**
         * Replaces the value currently associated with the key with the given value.
         * 为key设置新值。并返回旧值
         */
        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        /**
         * 比较节点是否相等(类型、key、value三者都相同,才相等)
         */
        public boolean equals(Object o) {
            //先判断类型是否相同
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            //然后判断key和value是否分别都相同。
            return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
        }

        /**
         * 计算节点的hash码
         */
        public int hashCode() {
            //计算key的hash码
            int keyHash = (key==null ? 0 : key.hashCode());
            //计算value的hash码
            int valueHash = (value==null ? 0 : value.hashCode());
            //按位异或 
            return keyHash ^ valueHash;
        }

        public String toString() {
            return key + "=" + value;
        }
    }

2.构造器

    public TreeMap() {
        comparator = null;
    }
    //构造器(使用传入的比较器)
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
    //构造器(不使用比较器)
    public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null;
        putAll(m);
    }
    //构造器(使用传入的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) {
        }
    }

3.put操作

    /**
     * 0.首次添加:直接以传入的key,value来建立根节点。
     * 1.非首次添加:查找待插入节点要挂载的父节点位置:从根节点开始依次将待插入节点和节点进行比较,直到找到合适的插入位置(父节点)
     *         ①.若使用了比较器,则使用比较器进行比较
     *         ②.若未使用比较器,则使用每个节点自己的比较功能比较
     *       2.使用传入的key,value创建新节点,并将其挂载到插入位置节点处。
     *       3.插入后修复。重新调整树结构和颜色,以满足红黑树的要求
     */
    public V put(K key, V value) {
        Entry<K,V> t = root;
        //0.首次添加。以<key,value>来建立根节点
        if (t == null) {
            //key的类型检查和非空检查
            compare(key, key); // type (and possibly null) check
            
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;          
            return null;//返回旧节点,显然为空
        }
        
        //1.说明是非首次添加
        
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        
        //1①比较器不为空。从根节点开始查找,使用比较器进行比较,直到找到插入位置
        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);
        }//1②比较器为空。从根节点开始查找,使用节点自身的比较功能进行比较 ,直到找到插入位置
        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);
        }
        //2.使用传入的key,value创建节点,并将其挂到合适的父节点下
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        //3.插入后修复
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

    /** From CLR */
    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) {
                    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;
    }

4.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);
        //否则,说明使用的是key的自然顺序。
        //不允许键为空,否则抛出异常
        if (key == null)
            throw new NullPointerException();
        Comparable<? super K> k = (Comparable<? super K>) key;//【能强转,说明key本身就实现了Camparable接口】
        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;
    }

总结

1.什么是红黑树?

2.TreeMap中比较器的作用?

TreeMap使用比较器来维持元素的顺序。

在构造TreeMap时,如果传入了比较器或者带比较功能的Map,则使用比较器来比较元素。否则,比较时使用元素自身的比较功能。

3.put操作的实现?get操作的实现?

4.元素是否允许null?

key为null时会抛出NullPointerException

原文地址:https://www.cnblogs.com/rouqinglangzi/p/6908512.html