HashMap分析之紅黑樹Key是如何compare的
阿新 • • 發佈:2022-05-17
前言
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方法