HashMap源码解析-红黑树操作

距离上一篇分析HashMap的文章已经过去一年了,今天偶尔翻到之前的那篇,还记得当时是打算另起一篇来分析链表的树化,以及树的链表化过程,结果这一拖就???,还是来把坑填上吧,毕竟要有始有终。

在我看来HashMap大致分为三个部分:散列表,红黑树算法,链表与树之间的转化。关于散列表部分,关于红黑树分析,这里就来分析下树化部分。

接下来先分析TreeNode 中的各个方法,之后再在相关操作中看它们的应用。

    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
   
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
   
            super(hash, key, val, next);
        }

TreeNode 继承自 LinkedHashMap.Entry,LinkedHashMap.Entry 继承自HashMap.Node,这绕了一圈是因为LinkedHashMap 的实现是依赖于HashMap的。关于LinkedHashMap的解析看我的这篇:LinkedHashMap源码解析(JDK8)

对于TreeNode 它有指向父节点的 parent,指向左节点的 left,右节点 right,前一个节点 prev,以及继承自 Node 的next,还有LinkedHashMap.Entry 中的 before,after指针,这两个与插入顺序的实现有关,在HashMap中并无对二者的任何相关操作。这就造成我们可以从两种不同视图角度来看树结构,一个是 parent,left,right 形成的二叉树,另一个是 next ,previous 形成的双向链表

moveRootToFront:
就是将 root 前移至链头,不改变其在树中的位置。该方法是为了确保 root 即是红黑树的根节点,又是双向链表的表头。因为插入,删除操作可能会改变树的根节点,在插入删除后会调用该方法确保这种一致性,只有一处在删除后没有调用该方法,后文再介绍。
该方法本质上就是将节点从双向链原位置处删除,之后插入到链表的头部。

        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
   
            int n;
            if (root != null && tab != null && (n = tab.length) > 0) {
   
                int index = (n - 1) & root.hash;
                TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
                if (root != first) {
   
                    Node<K,V> rn;
                    tab[index] = root;
                    TreeNode<K,V> rp = root.prev;
                    if ((rn = root.next) != null)
                        ((TreeNode<K,V>)rn).prev = rp;
                    if (rp != null)
                        rp.next = rn;
                    if (first != null)
                        first.prev = root;
                    root.next = first;
                    root.prev = null;
                }
                assert checkInvariants(root);
            }
        }

treeify

在介绍 treeify 方法之前先来介绍下 TreeNode 节点之间的大小比较,假设要插入的节点 x,其关键字为 k,比较位置处节点 p,其关键字 pk :首先比较 x 与 p 的 hash字段(即 key 的hash),若相同则要看 k 的类是否是 class X implements Comparable<X>这样的类型,是则调用 compareTo( pk ),前提是 pk 与 k 是同一类型(即 k.getClass == pk.getClass),否则则比较二者的类名,若仍相同则比较二者对象的hashCode,若 k 对象的hashCode 小于等于 pk 的,则返回 -1,代表 x 应插入到 p 的左子树中。

上述说明的实现对应 comparableClassFor,compareComparables,tieBreakOrder 三个方法:

comparableClassFor:
如果 x 类是这样的 :class X implements Comparable<X>,则返回 x 的class 对象,否则返回null。

    static Class<?> comparableClassFor(Object x) {
   
        if (x instanceof Comparable) {
    // x必须实现Comparable接口
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
            // String的化直接返回String.class
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            // 获取x直接实现的所有接口
            if ((ts = c.getGenericInterfaces()) != null) {
   
            	// 遍历这些接口,找寻是否存在 Comparable<X>
                for (int i = 0; i < ts.length; ++i) {
   
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType)t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                        return c;
                }
            }
        }
        return null;
    }

关于 ParameterizedType 泛型参数化类型:ParameterizedType详解

compareComparables:
若 x 与 k 是同一个类,k 的类是这样的:class C implements Comparable<C>,
则调用 compareTo 方法进行比较,否则返回0。

    static int compareComparables(Class<?> kc, Object k, Object x) {
   
        return (x == null || x.getClass() != kc ? 0 :
                ((Comparable)k).compareTo(x));
    }

tieBreakOrder
若 a ,b 对象其中有一个为null,或者是同一个类,则比较对象的hashCode;否则比较它们的类名。System.identityHashCode(null) 大小为 0。

        static int tieBreakOrder(Object a, Object b) {
   
            int d;
            if (a == null || b == null ||
                (d &
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值