Map接口 -- TreeMap源码分析篇

119 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Map接口之TreeMap(有序Map)

有序map,结构为一个平衡二叉树,简称红黑树,TreeMap底层维护这一个Entry<K,V>对象

1.底层结构Entry<K,V>

K key;
V value;
Entry<K,V> left; //左节点
Entry<K,V> right; //右节点
Entry<K,V> parent; //父节点
boolean color = BLACK; //节点颜色

看到红黑树就不得不说一下红黑树的特性了

(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 (注意:这里叶子节点,是指为空的叶子节点!)
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

2.构造方法

 //无参
    public TreeMap() {
        comparator = null;
    }
    //自定义排序实现
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

3.put方法添加元素

 public V put(K key, V value) {
        Entry<K,V> t = root;
        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)
                    //新增节点的key小于当前节点的key,则添加到当前节点的左节点
                    t = t.left;
                else if (cmp > 0)
                    //大于 就添加到当前节点的右节点
                    t = t.right;
                else
                    //新旧key值相等 直接覆盖并返回新值
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            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;
    }

上面看到已经将新增元素插入到了新的节点了,然后交由fixAfterInsertion调整平衡,代码就不贴了贴个红黑树原理的地址, 讲的很透彻

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);
        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);
            //位置在当前节点前面 则走左节点 否则右节点 时间复杂打o(n)
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
    }

5.remove方法删除元素

 //前面是先获取到指定元素节点
 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

        // 需要替换节点(如果存在)需要重组
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);

        if (replacement != null) {
            // 节点重组到父级
            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;

            // 情况连接 以便fixAfterDeletion重新计算旋转和着色
            p.left = p.right = p.parent = null;

            // Fix replacement
            if (p.color == BLACK)
                fixAfterDeletion(replacement);
        } else if (p.parent == null) { //如果p节点是仅有的节点 根目录设置为null
            root = null;
        } else { // 下面没有节点的
         //如果删除节点是黑色节点 需要旋转和着色
            if (p.color == BLACK)
                fixAfterDeletion(p);
   //把p节点的引用设置为null 让jvm回收
            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;
            }
        }
    }

以上就是 Map接口 -- TreeMap源码分析篇 的全部内容了。 下一篇 Map接口 -- HashTable源码分析篇

既然选择了远方,便只顾风雨兼程