JAVA源码学习之集合-TreeMap

前言

从这章开始我们将进入到集合的最后一个模块, Map, 本章我们开始了解TreeMap

正文

类的描述

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

基于 NavigableMap实现红黑树。Map是根据它的键的 natural ordering排序,或通过设置在Map创建时 Comparator,取决于使用哪个构造函数。

这种实现提供保证log(n)的时间成本的containsKeygetputremove操作。算法是在Cormen,Leiserson改编,和Rivest的算法导论。

请注意,与任何排序映射一样,树映射维护的顺序,以及是否提供了显式比较器,如果此排序映射要正确实现映射接口,则必须与equals一致。(参见Comparable 或Comparator ,了解与等于一致的精确定义。)这是因为映射接口是根据equals操作定义的,但排序映射使用其compareTo(或compare)方法执行所有键比较,因此从排序映射的角度来看,此方法认为相等的两个键是相等的。排序映射的行为定义良好,即使其顺序与equals不一致;只是没有遵守地图界面的总合同。

请注意,此实现不同步。如果多个线程访问一个Map的同时,并至少有一个线程修改Map的结构,它必须是同步的外部。(结构上的修改是任何操作,添加或删除一个或多个映射;仅仅改变一个现有的键所关联的值不是一个结构上的修改。)这通常是由一些对象同步自然封装图完成。如果该对象不存在,Map应该是“包裹”使用Collections.synchronizedSortedMap方法。最好的做法是在创建时,防止意外的非同步访问的Map:

SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));

迭代器返回的集合的iterator方法返回这个类的所有的“集合视图的方法“快速失败:如果Map的结构修改,迭代器创建后的任何时间,以任何方式除了通过迭代器的remove方法,迭代器将抛出一个ConcurrentModificationException。因此,在遇到同步修改,迭代器不能快速、干净,而不是冒着任意的,非在将来一个不确定的时间确定的行为。

注意迭代器不能快速失败行为得到保证的话,一般来说,不可能在不同步的并发修改的存在作出难以保证。快速失败迭代器扔ConcurrentModificationException尽最大努力的基础上。因此,要写一个程序,依靠这一例外的正确性错误:快速失败迭代器的行为只能用来检测错误。

对所有Map.Entry返回这类及其观点方法代表快照映射在当时产生的。他们支持Entry.setValue方法。(注意,可以在相关的Map使用put。变化映射)

通过上面我们得知,TreeMap是通过红黑树来实现的,所以想要了解TreeMap,首先我们需要了解红黑数

请参考这篇文章

(1条消息) 红黑树原理及代码实现_花葬-天空之城的博客-CSDN博客

常量、变量

   //比较方法
    private final Comparator<? super K> comparator;

    //树的根节点
    private transient Entry<K,V> root;

    /**
     * map的节点数
     */
    private transient int size = 0;

    /**
     * 对树进行结构修改的次数。
     */
    private transient int modCount = 0;
   
   //红色: false
   private static final boolean RED   = false;
   //黑色: true
   private static final boolean BLACK = true;

节点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; //节点颜色 true 黑色。 false 红色

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

        //设置entry的值
        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;
        }
    }

构造方法

/**
 * 构造一个新的,空的树映射,使用它的键的自然顺序。 
 */ 
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);  //在下面介绍
    }

   /**
    构造一个包含相同映射的新树映射,并使用指定的排序映射使用相同的排序。 
    */
   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) {
        }
    }

使用了buildFromSorted, 看下他做了什么操作

   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);
    }
 /**
 * 给定树中节点个数,计算红色节点高度
 * @param sz 树中节点个数
 * @return
 */
 private static int computeRedLevel(int sz) {
        int level = 0;
        for (int m = sz - 1; m >= 0; m = m / 2 - 1)
            level++;
        return level;
    } 

该方法的返回值实际上是树的最高层满二叉树的高度+1。例如以下树: 

 其中节点个数为5,则红色节点的高度为2

  /**
 * 递归构建红黑树
 * @param level 当前树的高度,初始为0
 * @param lo 树中第一个元素的下标,初始为0
 * @param hi 树中最后一个元素的下标,初始为size-1
 * @param redLevel 红色节点的高度
 * @param it 迭代器,如果不为空,则从迭代器中读取元素
 * @param str 对象输入流,如果不为空,则从流中读取键值对
 * @param defaultVal value默认值
 * @return
 * @throws java.io.IOException
 * @throws ClassNotFoundException
 */

    @SuppressWarnings("unchecked")
    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 {
        /*
         * Strategy: The root is the middlemost element. To get to it, we
         * have to first recursively construct the entire left subtree,
         * so as to grab all of its elements. We can then proceed with right
         * subtree.
         *
         * The lo and hi arguments are the minimum and maximum
         * indices to pull out of the iterator or stream for current subtree.
         * They are not actually indexed, we just proceed sequentially,
         * ensuring that items are extracted in corresponding order.
         */

        if (hi < lo) return null;

        int mid = (lo + hi) >>> 1;

        Entry<K,V> left  = null;
        if (lo < mid)
            left = buildFromSorted(level+1, lo, mid - 1, redLevel,
                                   it, str, defaultVal);

        // extract key and/or value from iterator or stream
        K key;
        V value;
        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);

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

 模拟下TreeMap(SortedMap<K, ? extends V> m)场景,最后形成的数据结构是这样的

TreeMap<Integer, Integer> treeMap =  new TreeMap<>();
		treeMap.put(1, 1);
		treeMap.put(2, 2);
		treeMap.put(3, 3);
		treeMap.put(4, 4);
		treeMap.put(5, 5);

TreeMap<Integer, Integer> treeMap1 = new TreeMap<>(treeMap);

 put

ublic V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) { //根节点为空
            compare(key, key); // 判断key是否为空,为空错

            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) { //比较方法不为空
           //通过比较方法从根节点一直往下找与当前key一致就替换value
            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);  //一直找到nul节点
        }
        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节点比较,来设置节点的位置
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e); //进行红黑树自平衡处理
        size++;
        modCount++;
        return null;
    }

 public void putAll(Map<? extends K, ? extends V> map) {
        int mapSize = map.size();
         //如果是SortedMap且map.size >0,并且当前 this.size==0 走buildFromSorted
        if (size==0 && mapSize!=0 && map instanceof SortedMap) { 
            Comparator<?> c = ((SortedMap<?,?>)map).comparator();
            if (c == comparator || (c != null && c.equals(comparator))) {
                ++modCount;
                try {
                    buildFromSorted(mapSize, map.entrySet().iterator(),
                                    null, null);
                } catch (java.io.IOException cannotHappen) {
                } catch (ClassNotFoundException cannotHappen) {
                }
                return;
            }
        }
        super.putAll(map);
    }

//否则循环走put逻辑
public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }
 //这个方法上面红黑树中有详细的介绍,这里就不多做解释了
 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;
    }

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

//通过红黑树方法查找key的位置
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;
    }

remove先通过红黑树的方法找到Entry,然后调用deleteEntry

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

       ...
    }

我们先看下 successor

//找到最接近大于T的数
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
        if (t == null)
            return null;
       //从t的右子树中找到最小的
        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;
        }
    }

从上面可以知道当p有两个子节点时,找到最接近大于T的数,然后将p直接替换为这个数,继续往下

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

        ....

        // 获取p的子节点中不为空的节点,左子节点先判断, 也可能两个都为空
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);

        if (replacement != null) { //存在子节点
            // Link replacement to parent
            //将子节点替换p位置
            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;

            //清空p的指针
            p.left = p.right = p.parent = null;

            // Fix replacement
            if (p.color == BLACK)
                fixAfterDeletion(replacement); 
        } else if (p.parent == null) { //判断是不是根节点,且只有一个元素,
            root = null; //根节点直接为空
        } else { //没有子节点
            if (p.color == BLACK) //当前节点如果是黑色
                fixAfterDeletion(p);

            if (p.parent != null) { //p.parent不为空,判断p是p.parent,那个节点,将相关子节点设置为null, 将p指向parent的置为null 是为了清空链接,方便GC
                if (p == p.parent.left) 
                    p.parent.left = null;
                else if (p == p.parent.right)
                    p.parent.right = null;
                p.parent = null; 
            }
        }
    }

上面可以看到当p的color说BLACK时会调用fixAfterDeletion

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);
    }

 这个我有点看不懂,找了一文章,讲的很好

Java提高篇(二七)-----TreeMap_chenssy 的技术博客-CSDN博客_treemap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值