HashMap分析之红黑树Key是如何compare的

前言

Java中HashMap使用链地址法来解决hash冲突,底层使用数组加链表的结构,Java8之后,对底层结构进行了优化。
当链表长度大于8时,就会转换成红黑树(如果此时数组长度小于64,先扩容),
当红黑树节点数量小于6时,再次转换成链表。

但我们知道,红黑树是一个二叉搜索树,所以要求Map的Key必须是可比较的,这里我们来分析一下具体是如何比较的。

原理分析

第一次比较

static Class<?> comparableClassFor(Object x) {
        if (x instanceof Comparable) {
            Class<?> c; Type[] ts, as; ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            //获取泛型接口的参数类型
            if ((ts = c.getGenericInterfaces()) != null) {
                for (Type t : ts) {
                    if ((t 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;
    }

如果key是Comparable类型,且实现的接口为泛型接口,如下面这样

class User implements Comparable<User> {
    @Override
    public int compareTo(User o) {
      return 0;
    }
}

而不是普通接口,如下面这种。

class User implements Comparable {
    @Override
    public int compareTo(Object o) {
      return 0;
    }
}

如果类型为Comparable,直接调用Comparable的compareTo()方法比较。

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

第二次比较

如果key类型不是Comparable,或者Comparable比较结果为0(表示相等),再通过下面的方法进行比较

static int tieBreakOrder(Object a, Object b) {
            int d;
            if (a == null || b == null ||
                (d = a.getClass().getName().
                 compareTo(b.getClass().getName())) == 0)
                d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                     -1 : 1);
            return d;
        }

使用所属Class名称比较,如果类型不同(如果我们没有使用泛型,key可以为不同类型),就比较两个对象的hashCode值(调用native方法获取)。

参考

讨论一下HashMap链表最大长度问题
HashMap链表树化时机 : (binCount >= TREEIFY_THRESHOLD - 1)
Java 8系列之重新认识HashMap
JDK8:HashMap源码解析:comparableClassFor、compareComparables、tieBreakOrder方法

posted @ 2022-05-17 19:03  strongmore  阅读(767)  评论(0)    收藏  举报