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方法

浙公网安备 33010602011771号