1. 程式人生 > 其它 >HashMap分析之紅黑樹Key是如何compare的

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