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> 那麼返回空
}
/**
* 如果x所屬的類是kc,返回k.compareTo(x)的比較結果
* 如果x爲空,或者其所屬的類不是kc,返回0
*/
@SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
static int compareComparables(Class<?> kc, Object k, Object x) {
    return (x == null || x.getClass() != kc ? 0 :
            ((Comparable)k).compareTo(x));
}

如果兩者不具有compare的資格,或者compare之後仍然沒有比較出大小。那麼就要通過一個決勝局再比一次,這個決勝局就是tieBreakOrder方法。

/**
* 用這個方法來比較兩個對象,返回值要麼大於0,要麼小於0,不會爲0
* 也就是說這一步一定能確定要插入的節點要麼是樹的左節點,要麼是右節點,不然就無法繼續滿足二叉樹結構了
* 
* 先比較兩個對象的類名,類名是字符串對象,就按字符串的比較規則
* 如果兩個對象是同一個類型,那麼調用本地方法爲兩個對象生成hashCode值,再進行比較,hashCode相等的話返回-1
*/
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;
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章