HashMap源碼-概述

Implementation Notes

  • HashMap有兩個(gè)參數(shù),initialCapacity(默認(rèn)16),loadFactor默認(rèn)0.75,當(dāng)容器內(nèi)節(jié)點(diǎn)數(shù)量多于initialCapacity*loadFactor,自動(dòng)擴(kuò)充
  • loadFactor越大,時(shí)間(puth和get的時(shí)間)成本越高,越小,空間成本越高
  • 一般情況下,內(nèi)部存儲(chǔ)的是哈希表,當(dāng)內(nèi)容過大時(shí),轉(zhuǎn)變?yōu)門reeNode容器,TreeNode容器內(nèi)部類似TreeMap的結(jié)構(gòu),HashMap的方法大部分不區(qū)分,只在TreeNode有額外實(shí)現(xiàn)時(shí)被調(diào)用(通過instrance of TreeNode)方法,使用TreeNode容器是為了在數(shù)據(jù)過多時(shí)能夠快速查找,然而因?yàn)榇蟛糠諱ap內(nèi)部元素沒有達(dá)到需要Tree Bin(樹狀容器)存儲(chǔ)的要求(默認(rèn)64個(gè)元素),所以checking for existence of tree bins may be delayed in the course of table methods.
  • 樹形態(tài)根據(jù)key的哈希碼排序,但是當(dāng)類實(shí)現(xiàn)了Comparable接口時(shí),類的compareTo方法被調(diào)用來排序,在多個(gè)實(shí)例返回同樣的hashCode的情況下,通過實(shí)現(xiàn)compareTo方法來提高效率
  • TreeNode大小大概是普通Node的兩倍,因此需要一個(gè)閾值來控制它的啟用

常量

  • DEFAULT_INITIAL_CAPACITY 初始化容量,16
  • MAXIMUM_CAPACITY 最大容量,2的30次方
  • DEFAULT_LOAD_FACTOR 負(fù)載比例,0.75
  • TREEIFY_THRESHOLD 樹狀閾值,HashMap相同hash的默認(rèn)放到同一個(gè)節(jié)點(diǎn)并next鏈時(shí)穿起來,但當(dāng)鏈長>=TREEIFY_THRESHOLD時(shí),需將鏈變?yōu)闃錉钜蕴嵘L問效率
  • UNTREEIFY_THRESHOLD (樹狀轉(zhuǎn)數(shù)組的閾值,6)
  • MIN_TREEIFY_CAPACITY 最小轉(zhuǎn)樹狀閾值,64

內(nèi)部靜態(tài)Node類

  • 內(nèi)部存儲(chǔ)final hash,final key,value,next四個(gè)變量
  • hashCode方法返回Objects.hashCode(key) ^ Objects.hashCode(value)
public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
  • equals方法要求key和value對(duì)equals方法都成立

方法

1.hash方法

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

計(jì)算key的hash,結(jié)尾是key的hashCode方法返回值(int型)的高16位拼接高16位與低16位按位異或的結(jié)果
這么做的原因是:table中計(jì)算key對(duì)應(yīng)存儲(chǔ)位置的時(shí)候,使用的是(capacity-1)&hash,當(dāng)n=16時(shí),相當(dāng)于16&hash,也就是,000000000000000001111&hash,也就是前28位確認(rèn)為0,hash的后四位決定是否碰撞,對(duì)于只在高位有區(qū)別的key,是大概率會(huì)碰撞的,因此將高16位spread到低位去,可以在某些場景減少碰撞,下面是驗(yàn)證:

public static void main(String[] args) {
        Float f1 = 11111.0f;
        Float f2 = 111111.0f;
        int capacity = 16;
        System.out.println("f1的二進(jìn)制:"+Integer.toBinaryString(f1.hashCode()));
        System.out.println("f2的二進(jìn)制:"+Integer.toBinaryString(f2.hashCode()));
        System.out.println("HashMap630行,位置計(jì)算方式為(n - 1) & hash,假設(shè)當(dāng)前容量為16\n若不進(jìn)行spread,則:");
        int index1 = (capacity-1)&f1.hashCode();
        int index2 = (capacity-1)&f2.hashCode();
        System.out.println("位置1為:"+index1);
        System.out.println("位置2為:"+index2);
        System.out.println("發(fā)生碰撞\n若進(jìn)行spread:");
        index1 = (capacity-1)&spreadHash(f1.hashCode());
        index2 = (capacity-1)&spreadHash(f2.hashCode());
        System.out.println("位置1為:"+index1);
        System.out.println("位置2為:"+index2);
        System.out.println("不發(fā)生碰撞");
    }

    /**
     * 按hashMap的方法,計(jì)算spread后的hash
     * @param hash
     * @return
     */
    static  int spreadHash(int hash){
        return hash ^ (hash >>> 16);
    }

f1的二進(jìn)制:1000110001011011001110000000000
f2的二進(jìn)制:1000111110110010000001110000000
根據(jù)HashMap源碼630行,位置計(jì)算方式為(n - 1) & hash,假設(shè)當(dāng)前容量為16
若不進(jìn)行spread,則:
位置1為:0
位置2為:0
發(fā)生碰撞
若進(jìn)行spread:
位置1為:13
位置2為:9
不發(fā)生碰撞

為什么是^不是|或者&,驗(yàn)證可得按&是不好的,比如上面例子,按位與的話,還是會(huì)碰撞(結(jié)果都是0),^和|的運(yùn)算結(jié)果一致,待探究

2.comparableClassFor方法:判斷是否實(shí)現(xiàn)Comparable接口

static Class<?> comparableClassFor(Object x)

  • 因?yàn)镾tring類型的key最多,且實(shí)現(xiàn)了Comparable接口,所以入?yún)镾tring類的直接返回String.class,
  • 判斷類實(shí)現(xiàn)Comparable接口,且泛型實(shí)際類型為自己,則返回
  • 否則返回null

3.compareComparables方法:返回兩個(gè)Comparable對(duì)象的比較值

static int compareComparables(Class<?> kc, Object k, Object x)

  • kc:實(shí)現(xiàn)了Comparable接口的實(shí)體類
  • k:對(duì)象1 c:對(duì)象2
  • 通過調(diào)用時(shí)保證類型正確
  • x為空或者不是kc類型時(shí)返回空,否則返回k.compareTo(x)

4.tableSizeFor:HashMap的方法,計(jì)算目標(biāo)容量對(duì)應(yīng)的2的次方數(shù)容量

static final int tableSizeFor(int cap)

內(nèi)部變量

1.transient Node<K,V>[] table;

核心存儲(chǔ)變量,隨需要resize,初始化時(shí),容量大小是2的次方

2.transient Set<Map.Entry<K,V>> entrySet;

保存緩存的entrySet

3.transient int size;

存儲(chǔ)當(dāng)前map的大小

4.transient int modCount;

存儲(chǔ)當(dāng)前map發(fā)生Structural modifications的次數(shù),用于在迭代時(shí)快速失?。╢ail-fast)

5.int threshold;

下一次resize的閾值,比如當(dāng)前threshold為16,當(dāng)前要放17個(gè)元素進(jìn)去,則需要resize

6.final float loadFactor;

負(fù)載因子

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 親愛的家長您好! 又一周過去了! 本周依然是語文版塊,我們繼續(xù)享受故事和漢字的工作,故事時(shí)間總是孩...
    0懶亮亮0閱讀 334評(píng)論 0 1
  • 聽說,孤獨(dú)是一個(gè)人的狂歡。其實(shí),歡樂永遠(yuǎn)和孤獨(dú)者無關(guān)。 夜色漸重,黑暗趁人不備將我重重地包圍了起來。 濃重的黑暗,...
    聽雨Q晴閱讀 165評(píng)論 0 2
  • 從你的專業(yè)角度來說,你所見過的最糟糕的建議是什么?“追隨你的夢想?!睕]有多年的自我認(rèn)知,你根本不可能做到這點(diǎn)。你是...
    紫苑閱讀 1,112評(píng)論 5 18

友情鏈接更多精彩內(nèi)容