sheng的学习笔记-TreeMap源码分析(源码版本16)

本文深入剖析了TreeMap的数据结构和工作原理,重点介绍了红黑树的使用,并对比了TreeMap与HashMap的区别,包括排序、性能及线程安全性等方面。

TreeMap使用

TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;
TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现;
TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;
TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序;
 

TreeMap根据Key来进行排序,如果不是对象,自然排序,如果是对象:

  • 在构造函数中传入比较器comparator,用comparator来比较元素的Key,用于排序
  • 或者key的对象实现Comparable,用Comparable的比较方法进行比较key

这东西本质上,就是比较key,然后用红黑树进行存储,查找

key对象实现Comparable代码示例

用comparator比较器的代码示例

 如果同时实现了comparator和Comparable,comparator优先级高

红黑树底层原理

由于TreeMap底层使用红黑树处理,想要了解内容必须先了解红黑树,但红黑树还需要知晓二叉树,平衡二叉树,2-3-4树的原理,可以看以下文章用于参考

sheng的学习笔记-二叉树(BST)_coldstarry的博客-优快云博客

sheng的学习笔记-平衡二叉树(AVL)和3+4重构_coldstarry的博客-优快云博客

sheng的学习笔记-2-3查找树_coldstarry的博客-优快云博客

sheng的学习笔记-红黑树_coldstarry的博客-优快云博客

TreeMap和HashMap比较:

(1)HashMap:适用于在Map中插入、删除和定位元素。 
(2)Treemap:适用于按自然顺序或自定义顺序遍历键(key)。 
(3)HashMap通常比TreeMap快一点(树和哈希表的数据结构使然),正常可以使用HashMap,在需要排序的Map时候才用TreeMap. HashMap的时间复杂度是O(1),是通过哈希函数计算的哈希地址。而我们的红黑树就不具有这样的优势时间复杂度是O(log2 n)
(4)HashMap 非线程安全 TreeMap 非线程安全 
(5)HashMap的结果是没有排序的,而TreeMap输出的结果是排好序的。

上面是拷过来的,总体来说HashMap底层是哈希桶(无序),TreeMap底层是红黑树(根据key排序,有序),如果想要KEY=>VALUE使用,可以用HashMap

代码解析

内部节点,comparator如果有,用于比较key值。用root存放红黑树

 

插入元素

起手式这个if就是根据comparator比较元素,如果没比较器,用comparable比较,使用的是红黑树的查找方式,找到需要插入新元素的位置,比如计划插入到叶子结点t的下面,此时t节点的left/right是空的,跳出while循环,但需要记住t节点,所以用Parent = t;这行代码

private V put(K key, V value, boolean replaceOld) {
        Entry<K,V> t = root;
        if (t == null) {
            addEntryToEmptyMap(key, value);
            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 {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        } else {
            Objects.requireNonNull(key);
            @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 {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        }
        addEntry(key, value, parent, cmp < 0);
        return null;
    }

 插入数据,addToLeft就是新节点比父节点大还是小,用于决定插入左边还是右边,插入完成后要进行红黑树的重新染色和旋转

 这就是重新染色和旋转了,具体的看红黑树部分,不细说了,看下面的博客或者自己找下博客看看

sheng的学习笔记-红黑树_coldstarry的博客-优快云博客

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

查找元素

查找元素就是个标准的二叉树查找(二叉树的查找和红黑树的查找一样,因为在查找时与节点的颜色无关) 。右比较器,通过比较器比较,否则通过comparable比较

 

 

删除元素

 先查找,再删除,删除中包括元素删除和重新染色,旋转

 删除逻辑

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

删除后重新染色逻辑

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

参考文章:

TreeMap原理(浅谈)_Android_la的博客-优快云博客_treemap原理

java中的comparator_Java中Comparator的使用_jeremy chang的博客-优快云博客

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值