JDK8:HashMap源码解析:comparableClassFor、compareComparables、tieBreakOrder方法

一、概述

在之前的文章里已经分析过,在发生hash碰撞(多个key的hash值相同)的时候,hashMap首先会采用链表进行存储,当链表节点数量达到一定阈值(8)会将链表上的节点再组织成一棵红黑树。红黑树是一种二叉树,每个父节点可以由左右两个节点。

当put一个新元素时,如果该元素键的hash值小于当前节点的hash值的时候,就会作为当前节点的左节点;hash值大于当前节点hash值得时候作为当前节点的右节点。那么hash值相同的时候呢?这时还是会先尝试看是否能够通过Comparable进行比较一下两个对象(当前节点的键对象和新元素的键对象),要想看看是否能基于Comparable进行比较的话,首先要看该元素键是否实现了Comparable接口,此时就需要用到comparableClassFor方法来获取该元素键的Class,然后再通过compareComparables方法来比较两个对象的大小。

二、方法解析
 

/**
* 如果对象x的类是C,如果C实现了Comparable<C>接口,那么返回C,否则返回null
*/
static Class<?> comparableClassFor(Object x) {
    if (x instanceof Comparable) {
        Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
        if ((c = x.getClass()) == String.class) // 如果x是个字符串对象
            return c; // 返回String.class
        /*
         * 为什么如果x是个字符串就直接返回c了呢 ? 因为String  实现了 Comparable 接口,可参考如下String类的定义
         * public final class String implements java.io.Serializable, Comparable<String>, CharSequence
         */ 
 
        // 如果 c 不是字符串类,获取c直接实现的接口(如果是泛型接口则附带泛型信息)    
        if ((ts = c.getGenericInterfaces()) != null) {
            for (int i = 0; i < ts.length; ++i) { // 遍历接口数组
                // 如果当前接口t是个泛型接口 
                // 如果该泛型接口t的原始类型p 是 Comparable 接口
                // 如果该Comparable接口p只定义了一个泛型参数
                // 如果这一个泛型参数的类型就是c,那么返回c
                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;
            }
            // 上面for循环的目的就是为了看看x的class是否 implements  Comparable<x的class>
        }
    }
    return null; // 如果c并没有实现 Comparable<c> 那么返回空
}
发布了69 篇原创文章 · 获赞 52 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/xzj80927/article/details/101055104
今日推荐