HashMap的底層實現(xiàn) 2020-06-08

本篇文章為個人學習筆記,參考自:https://blog.csdn.net/qq_41345773/article/details/92066554 有興趣的朋友可以去了解

一.概述

HashMap是基于Map接口實現(xiàn),元素以鍵值對的方式存儲辫愉,key允許為null 但是不允許重復

問題:HashMap是根據(jù)key的hashCode來尋找存放位置的拓诸,那當key為null時, 怎么存儲呢?
從源碼中我們就可以看到在put方法里頭肺樟,其實第一行就處理了key=null的情況:
if (key == null)  
   return putForNullKey(value);  
//那就看看這個putForNullKey是怎么處理的吧挎袜。  
private V putForNullKey(V value) {  
   for (Entry<K,V> e = table[0]; e != null; e = e.next) {  
       if (e.key == null) {  
           V oldValue = e.value;  
           e.value = value;  
           e.recordAccess(this);  
           return oldValue;  
       }  
   }  
   modCount++;  
   addEntry(0, null, value, 0);  
   return null;  
}  

可以看到顽聂,前面那個for循環(huán),是在talbe[0]鏈表中查找key為null的元素盯仪,如果找到紊搪,則將value重新賦值給這個元素的value,并返回原來的value全景,如果上面for循環(huán)沒找到則將這個元素添加到talbe[0]鏈表的表頭耀石,所以在HashMap中key為null的值都是放在數(shù)據(jù)的第0個位置。

二.基本屬性

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //默認初始化大小 16 
static final float DEFAULT_LOAD_FACTOR = 0.75f;     //負載因子0.75
static final Entry<?,?>[] EMPTY_TABLE = {};         //初始化的默認數(shù)組
transient int size;     //HashMap中元素的數(shù)量
int threshold;          //判斷是否需要調整HashMap的容量  

參數(shù)說明:
1. DEFAULT_INITIAL_CAPACITY
HashMap的默認初始化長度為16爸黄,因為HashMap的擴容是一項耗時的操作滞伟,所以如果能預估HashMap的長度,可以在構造函數(shù)中指定默認長度炕贵,但需要注意的是梆奈,HashMap的默認長度會為比你傳入長度值大并且最接近的二的次方的值,比如你默認長度為20称开,但實際HashMap的默認長度會為2的5次 即32亩钟;
2.DEFAULT_LOAD_FACTOR 負載因子
DEFAULT_LOAD_FACTOR 負載因子是表示Hsah表中元素的填滿的程度,若:加載因子越大,填滿的元素越多,好處是,空間利用率高了,但:沖突的機會加大了.鏈表長度會越來越長,查找效率降低乓梨。反之,加載因子越小,填滿的元素越少,好處是:沖突的機會減小了,但:空間浪費多了.表中的數(shù)據(jù)將過于稀疏(很多空間還沒用,就開始擴容了)
沖突的機會越大,則查找的成本越高.因此,必須在 "沖突的機會"與"空間利用率"之間尋找一種平衡與折衷. 這種平衡與折衷本質上是數(shù)據(jù)結構中有名的"時-空"矛盾的平衡與折衷.
hashMap默認的負載因子為0.75清酥,這是考慮到存儲空間和查詢時間上成本的一個折中值扶镀,增大負載因子,可以減少hash表(就是那個entry數(shù)組)所占用的內空間总处,但會增加查詢數(shù)據(jù)的時間開銷狈惫,而查詢是最頻繁的操作(put()和get()都用到查詢);減小負載因子鹦马,會提高查詢時間胧谈,但會增加hash表所占的內存空間。
3.size荸频、threshold
HashMap中元素的數(shù)量菱肖,當調用createEntry()方法后size會進行++操作;threshold指的是HashMap的閾值旭从,它的值為HashMap默認初始化值 * 負載因子稳强;如果size>threshold,HashMap就會調用resize()方法進行擴容,擴容后的長度為原來長度的2倍和悦;

/*
 * hash hash值
 * key 鍵值
 * value value值
 * bucketIndex Entry[]數(shù)組中的存儲索引
 * / 
void addEntry(int hash, K key, V value, int bucketIndex) {
     if ((size >= threshold) && (null != table[bucketIndex])) {
         resize(2 * table.length); //擴容操作退疫,將數(shù)據(jù)元素重新計算位置后放入newTable中,鏈表的順序與之前的順序相反
         hash = (null != key) ? hash(key) : 0;
         bucketIndex = indexFor(hash, table.length);
     }
 
    createEntry(hash, key, value, bucketIndex);
}
void createEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

三鸽素、HashMap的數(shù)據(jù)儲存結構

JDK1.7以前HashMap采用的是數(shù)組+鏈表的數(shù)據(jù)結構來存儲褒繁;JDK1.8中HashMap采用數(shù)組+鏈表+紅黑樹的數(shù)據(jù)結構來存儲。
以下為JDK1.7時HashMap的數(shù)據(jù)結構理解(借鑒他人博客)
HashMap采用Entry數(shù)組來存儲key-value對馍忽,每一個鍵值對組成了一個Entry實體棒坏,Entry類實際上是一個單向的鏈表結構,它具有Next指針遭笋,可以連接下一個Entry實體坝冕,以此來解決Hash沖突的問題。
數(shù)組存儲區(qū)間是連續(xù)的瓦呼,占用內存嚴重喂窟,故空間復雜的很大。但數(shù)組的二分查找時間復雜度小央串,為O(1)谎替;數(shù)組的特點是:尋址容易,插入和刪除困難蹋辅;
鏈表存儲區(qū)間離散,占用內存比較寬松挫掏,故空間復雜度很小侦另,但時間復雜度很大,達O(N)。鏈表的特點是:尋址困難褒傅,插入和刪除容易弃锐。

image.png

從上圖我們可以發(fā)現(xiàn)數(shù)據(jù)結構由數(shù)組+鏈表組成,一個長度為16的數(shù)組中殿托,每個元素存儲的是一個鏈表的頭結點霹菊。那么這些元素是按照什么樣的規(guī)則存儲到數(shù)組中呢。一般情況是通過hash(key.hashCode())%len獲得支竹,也就是元素的key的哈希值對數(shù)組長度取模得到旋廷。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12礼搁。所以12饶碘、28、108以及140都存儲在數(shù)組下標為12的位置馒吴。

HashMap里面實現(xiàn)一個靜態(tài)內部類Entry扎运,其重要的屬性有 hash,key饮戳,value豪治,next。

HashMap里面用到鏈式數(shù)據(jù)結構的一個概念扯罐。上面我們提到過Entry類里面有一個next屬性负拟,作用是指向下一個Entry。打個比方篮赢, 第一個鍵值對A進來齿椅,通過計算其key的hash得到的index=0,記做:Entry[0] = A启泣。一會后又進來一個鍵值對B涣脚,通過計算其index也等于0,現(xiàn)在怎么辦寥茫?HashMap會這樣做:B.next = A,Entry[0] = B,如果又進來C,index也等于0,那么C.next = B,Entry[0] = C遣蚀;這樣我們發(fā)現(xiàn)index=0的地方其實存取了A,B,C三個鍵值對,他們通過next這個屬性鏈接在一起。所以疑問不用擔心纱耻。也就是說數(shù)組中存儲的是最后插入的元素芭梯。到這里為止,HashMap的大致實現(xiàn)弄喘,我們應該已經(jīng)清楚了玖喘。

四、HashMap是如何解決同一hash值蘑志,不同key的問題

對于同一hash值累奈,不同key的沖突,HashMap是采用鏈表法來解決的贬派,即如果出現(xiàn)沖突,則將新加入的對象指向原來的對象澎媒,形成一個單鏈表搞乏。參考代碼如下:

void addEntry(int hash, K key, V value, int bucketIndex) {  
 Entry<K,V> e = table[bucketIndex];  
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);  //e 即下一個節(jié)點的entry值 即next
if (size++ >= [threshold](https://www.baidu.com/s?wd=threshold&tn=24004469_oem_dg&rsv_dl=gh_pl_sl_csd))  
resize(2 * table.length);  
 bsp;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市戒努,隨后出現(xiàn)的幾起案子请敦,更是在濱河造成了極大的恐慌,老刑警劉巖储玫,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侍筛,死亡現(xiàn)場離奇詭異,居然都是意外死亡缘缚,警方通過查閱死者的電腦和手機勾笆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桥滨,“玉大人窝爪,你說我怎么就攤上這事∑朊剑” “怎么了蒲每?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長喻括。 經(jīng)常有香客問我邀杏,道長,這世上最難降的妖魔是什么唬血? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任望蜡,我火速辦了婚禮,結果婚禮上拷恨,老公的妹妹穿的比我還像新娘脖律。我一直安慰自己,他們只是感情好腕侄,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布小泉。 她就那樣靜靜地躺著,像睡著了一般冕杠。 火紅的嫁衣襯著肌膚如雪微姊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天分预,我揣著相機與錄音兢交,去河邊找鬼。 笑死笼痹,一個胖子當著我的面吹牛配喳,可吹牛的內容都是我干的飘诗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼界逛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纺座?” 一聲冷哼從身側響起息拜,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎净响,沒想到半個月后少欺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡馋贤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年赞别,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片配乓。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡仿滔,死狀恐怖,靈堂內的尸體忽然破棺而出犹芹,到底是詐尸還是另有隱情崎页,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布腰埂,位于F島的核電站飒焦,受9級特大地震影響,放射性物質發(fā)生泄漏屿笼。R本人自食惡果不足惜牺荠,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望驴一。 院中可真熱鬧休雌,春花似錦、人聲如沸蛔趴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孝情。三九已至鱼蝉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間箫荡,已是汗流浹背魁亦。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留羔挡,地道東北人洁奈。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓间唉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親利术。 傳聞我的和親對象是個殘疾皇子呈野,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353