Treemap 源码分析

概念图如下:

 

我们来简要分析一下:

TreeMap 是由红黑树实现的有序的 key-value 集合。

上类图:

TreeMap 首先继承了 AbstractMap 抽象类,表示它具有散列表的性质,也就是由 key-value 组成。

  其次 TreeMap 实现了 NavigableMap 接口,该接口支持一系列获取指定集合的导航方法,比如获取小于指定key的集合。

  最后分别实现 Serializable 接口以及 Cloneable 接口,分别表示支持对象序列化以及对象克隆

 

源码细节分析:

①、Comparator

复制代码

    /**
     * The comparator used to maintain order in this tree map, or
     * null if it uses the natural ordering of its keys.
     *
     * @serial
     */
    private final Comparator<? super K> comparator;

复制代码

  可以看上面的英文注释,Comparator 是用来维护treemap集合中的顺序,如果为null,则按照key的自然顺序。

 Comparator 是一个接口,排序时需要实现其 compare 方法,该方法返回正数,零,负数分别代表大于,等于,小于

②、Entry

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;

        /**
         * 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;
        }
    }

 ③、size

    /**
     * The number of entries in the tree
     */
    private transient int size = 0;

  用来表示entry的个数,也就是key-value的个数。

 

④、modCount

    /**
     * The number of structural modifications to the tree.
     */
    private transient int modCount = 0;

  基本上前面讲的在ArrayList,LinkedList,HashMap等线程不安全的集合都有此字段,用来实现Fail-Fast 机制,如果在迭代这些集合的过程中,有其他线程修改了这些集合,就会抛出ConcurrentModificationException异常。

⑤、红黑树常量

    private static final boolean RED   = false;
    private static final boolean BLACK = true;

 

3.3、构造函数

③、构造包含指定map集合的元素

1     public TreeMap(Map<? extends K, ? extends V> m) {
2         comparator = null;
3         putAll(m);
4     }

  使用该构造器创建的TreeMap,会默认插入m表示的集合元素,并且comparator表示按照自然顺序进行插入。

  ④、带 SortedMap的构造函数

复制代码

1     public TreeMap(SortedMap<K, ? extends V> m) {
2         comparator = m.comparator();
3         try {
4             buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
5         } catch (java.io.IOException cannotHappen) {
6         } catch (ClassNotFoundException cannotHappen) {
7         }
8     }

复制代码

  和上面带Map的构造函数不一样,map是无序的,而SortedMap 是有序的,使用 buildFromSorted() 方法将SortedMap集合中的元素插入到TreeMap 中。

add 方法:

//添加元素
    public V put(K key, V value) {
        TreeMap.Entry<K,V> t = root;
        //如果根节点为空,即TreeMap中一个元素都没有,那么设置新添加的元素为根节点
        //并且设置集合大小size=1,以及modCount+1,这是用于快速失败
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new TreeMap.Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        TreeMap.Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        //如果比较器不为空,即初始化TreeMap构造函数时,有传递comparator类
        //那么插入新的元素时,按照comparator实现的类进行排序
        if (cpr != null) {
            //通过do-while循环不断遍历树,调用比较器对key值进行比较
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    //遇到key相等,直接将新值覆盖到原值上
                    return t.setValue(value);
            } while (t != null);
        }
        //如果比较器为空,即初始化TreeMap构造函数时,没有传递comparator类
        //那么插入新的元素时,按照key的自然顺序
        else {
            //如果key==null,直接抛出异常
            //注意,上面构造TreeMap传入了Comparator,是可以允许key==null
            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);
        }
        //找到父亲节点,根据父亲节点创建一个新节点
        TreeMap.Entry<K,V> e = new TreeMap.Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        //修正红黑树(包括节点的左旋和右旋,具体可以看我Java数据结构和算法中对红黑树的介绍)
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

添加元素,如果初始化TreeMap构造函数时,没有传递comparator类,是不允许插入key==null的键值对的,相反,如果实现了Comparator,则可以传递key=null的键值对。

。删除元素:

删除节点分为四种情况:

  1、根据key没有找到该节点:也就是集合中不存在这一个节点,直接返回null即可。

  2、根据key找到节点,又分为三种情况:

    ①、待删除节点没有子节点,即为叶子节点:直接删除该节点即可。

    ②、待删除节点只有一个子节点:那么首先找到待删除节点的子节点,然后删除该节点,用其唯一子节点顶替该节点。

    ③、待删除节点有两个子节点:首先找到该节点的中序后继节点,然后把这个后继节点的内容复制给待删除节点,然后删除该中序后继节点,删除过程又转换成前面①、②两种情况了,这里主要是找到中序后继节点,相当于待删除节点的一个替

public V remove(Object key) {
        //根据key找到该节点
        TreeMap.Entry<K,V> p = getEntry(key);
        if (p == null)
            return null;
        //获取该节点的value,并返回
        V oldValue = p.value;
        //调用deleteEntry()方法删除节点
        deleteEntry(p);
        return oldValue;
    }

    private void deleteEntry(TreeMap.Entry<K,V> p) {
        modCount++;
        size--;

        //如果删除节点的左右节点都不为空,即有两个孩子
        if (p.left != null && p.right != null) {
            //得到该节点的中序后继节点
            TreeMap.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.
        TreeMap.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);

            //TreeMap中只有待删除节点P,也就是只有一个节点,直接返回nul即可
        } 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;
            }
        }
    }

6.查找元素:

①、根据key查找

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

    final TreeMap.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;
        TreeMap.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;
    }

7.迭代元素:

可以使用迭代器完成。

 

 

问题: 为什么使用第二中方案呢?

因为效率高:

//第二种方法
15 Iterator<Map.Entry<String,Integer>> iterator1 = map.entrySet().iterator();
16 while(iterator1.hasNext()){
17     Map.Entry<String,Integer> entry = iterator1.next();
18     System.out.println(entry.getKey()+":"+entry.getValue());
19 }

 

 

 

参考博文:https://www.cnblogs.com/ysocean/p/10300976.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执于代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值