HashMap

HashMap

HashMap存儲(chǔ)的時(shí)key-value格式的實(shí)例。
底層的存儲(chǔ)結(jié)構(gòu)是數(shù)組+鏈表格式。
單個(gè)實(shí)例的格式

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

一些成員變量

transient Node<K,V>[] table:底層存儲(chǔ)結(jié)構(gòu)
Set<Map.Entry<K,V>> entrySet:
size:記錄了當(dāng)前數(shù)量
threshold:臨界值threshold = capacity * loadFactor,當(dāng)size大于臨界值就要擴(kuò)容
loadFactor:負(fù)載因子,衡量HashMap滿的程度,默認(rèn)0.75f

還有一個(gè)capacity容量,不是成員變量,但很重要。

新建HashMap

 public HashMap(int initialCapacity, float loadFactor) 
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

無參構(gòu)造函數(shù),在第一次put數(shù)據(jù)的時(shí)候會(huì)進(jìn)行擴(kuò)容。

newCap = DEFAULT_INITIAL_CAPACITY;//16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

上述構(gòu)造函數(shù)新建時(shí),并沒有新建數(shù)組,對于前2個(gè),只是設(shè)置了負(fù)載因子和臨界值。
對于無參的構(gòu)造函數(shù),在第一次put數(shù)據(jù)時(shí)才會(huì)設(shè)置負(fù)載因子和臨界值。
初始化數(shù)組都是在第一次put數(shù)據(jù)時(shí)。

存取原理

put方法

總體的流程就是,如果定位的table[index]為null,直接插入,如果index的key和要插入的相等(地址或者equals)那直接覆蓋。如果不是就鏈表或紅黑樹遍歷,遍歷完再走上面流程。


put流程

https://www.cnblogs.com/LiaHon/p/11149644.html

  1. 通過hash函數(shù)計(jì)算存儲(chǔ)位置
 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  1. 判斷table是否為null或長度為0,如果是,調(diào)用resize()方法進(jìn)行擴(kuò)容
  2. 如果tabel[i]為null,直接構(gòu)造Node插入
  3. 如果tabel[i]不為null 判斷key是否等于index處對應(yīng)的key,如果相等或者equals方法相等,直接覆蓋。
  4. 否則如果是紅黑樹,插入
  5. 鏈表的話判斷長度是否大于等于8,是的話變?yōu)榧t黑樹插入,否則鏈表 插入。

get方法

  1. table為null,返回null
  2. 計(jì)算hash值,定位到index,判斷key是否相等或者equals是否相等,如果相等返回
  3. 否則遍歷,如果是TreeNode紅黑樹,如果是鏈表,鏈表遍歷。

為什么需要負(fù)載因子:

因?yàn)閙ap映射需要進(jìn)行hash,如果負(fù)載因子是1的話,hash沖突概率變大。0.75是個(gè)經(jīng)驗(yàn)值。
如果小了,空間利用率低,如果大了,鏈表長度過長或者紅黑樹高度過高。

默認(rèn)長度為啥是16

經(jīng)驗(yàn)值,保證是2的次冪就行(1 << 4; // aka 16

為什么capatity大小都是2的冪?(真正的大小)

為了使用二進(jìn)制完成取模運(yùn)行,不用做進(jìn)制轉(zhuǎn)換,效率高。
hash函數(shù)
初始的閾值計(jì)算。

this.threshold = tableSizeFor(initialCapacity);

該方法是找到第一個(gè)比capacity大的2的次冪。
hash函數(shù)的原理還是取模
X % 2^n = X & (2^n – 1)
table中index的選擇方式:i = (n - 1) & hash
因此:只要保證length的長度是2^n 的話,就可以實(shí)現(xiàn)取模運(yùn)算了

https://www.cnblogs.com/hollischuang/p/12009172.html

擴(kuò)容方式

擴(kuò)容時(shí)機(jī)

第一次插入時(shí)擴(kuò)容

        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

容量不夠時(shí)擴(kuò)容

        if (++size > threshold)
            resize();

容量不足時(shí)擴(kuò)容的長度2倍

            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold

擴(kuò)容是插入步驟:

  1. 新建一個(gè)2倍數(shù)組Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
  2. 重新計(jì)算index插入newTab[e.hash & (newCap - 1)] = e;

頭插入尾插為啥形成環(huán)

頭插入的話,擴(kuò)容后,鏈表上實(shí)例的相對位置會(huì)發(fā)生變化。多線程環(huán)境下操作可能形成環(huán)。

線程安全相關(guān)

1.在jdk1.7中,在多線程環(huán)境下,擴(kuò)容時(shí)會(huì)造成環(huán)形鏈或數(shù)據(jù)丟失。
2.在jdk1.8中,在多線程環(huán)境下,會(huì)發(fā)生數(shù)據(jù)覆蓋的情況。

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

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

  • 1.HashMap的底層實(shí)現(xiàn)圖示 如上圖所示: HashMap底層是由數(shù)組+(鏈表)=(紅黑樹)組成,每個(gè)存儲(chǔ)在H...
    Bamboo_a67a閱讀 334評論 0 0
  • 0、HashMap 簡介 HashMap是由數(shù)組、鏈表或紅黑樹組成的,應(yīng)該是我們Java開發(fā)工作中用到的非常普遍的...
    小馬蛋閱讀 387評論 0 2
  • 本文從 Hash 方法開始,通過分析源碼,深入介紹了 JDK 不同版本中 HashMap 的實(shí)現(xiàn)。 HashMap...
    南風(fēng)過境jz閱讀 218評論 0 0
  • 一、前言 在分析jdk1.8后的HashMap源碼時(shí),發(fā)現(xiàn)網(wǎng)上好多分析都是基于之前的jdk,而Java8的Hash...
    程序人生a閱讀 367評論 0 1
  • 久違的晴天,家長會(huì)。 家長大會(huì)開好到教室時(shí),離放學(xué)已經(jīng)沒多少時(shí)間了。班主任說已經(jīng)安排了三個(gè)家長分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,810評論 16 22

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