HashMap實現原理分析(1)

從本文開始肝劲,介紹一下最常用的一個集合對象HashMap突雪,HashMap存儲的是鍵值對闪唆,本文采用的基于JDK11的源碼實現碧库。 一般大家都知道HashMap是通過put操作把一組鍵值對(key和value)存儲到HashMap中柜与,然后可以通過get(key)去獲取key對應的value。而最重要的這兩個過程是怎么實現的呢嵌灰?下面我們就來對put和get這兩個過程做一個分析弄匕。

HashMap基本工作原理

下面先看一段源碼:

/**
     * The table, initialized on first use, and resized as
     * necessary. When allocated, length is always a power of two.
     * (We also tolerate length zero in some operations to allow
     * bootstrapping mechanics that are currently not needed.)
 */
transient Node<K,V>[] table;

當用戶調用put方法的時候把key和value放入到HashMap的時候,這個數組table就是實際存儲key和value的地方沽瞭。HashMap把用戶傳入的key和value封裝成一個Node<K,V>對象迁匠,把該Node<K,V>對象放入到table對應的位置。Map執(zhí)行get操作的時候驹溃,并沒有傳入具體的數組的索引位置信息城丧,只是傳入了key,因此這個地方就會涉及到一個key轉索引的一個操作豌鹤,然后根據索引獲取table中對應位置的Node對象亡哄,把value值返回給用戶。由于數組的訪問時間復雜度是O(1)布疙,因此Map的get操作也可以認為是O(1)( 這個地方先暫時理解為O(1),具體原因見后面)蚊惯。

簡單來說,在執(zhí)行put方法的時候灵临,Map會根據傳入的key獲取它hashcode值拣挪,然后根據hashcode與table大小進行求模運算,得到的值就是它在table數組索引位置俱诸。實際這個過程又有點復雜,具體下面開始分析赊舶。

HashMap 數組尋址與hash值計算

用戶通過key訪問map獲取value的時候睁搭,原理是用key的hash值來與數組的大小取模獲取數組的索引赶诊。但實際在HashMap實現中,對取模運算進行了一下優(yōu)化园骆,采用了(n-1) & hash(key)的方法獲取數組索引舔痪,這里的n是table的大小,hash(key)表示key的哈希值锌唾,這種方法可以得到與取模運算一樣的效果锄码,但是速度要比取模運算快。

下面看一下晌涕,hash(key)的實現邏輯

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

從上面的源碼看:

  • 調用key的hashCode()方法獲取hashCode值h
  • 把h進行無符號右移16位
  • 把h與h右移后的值進行異或操作最后得到key的hash值滋捶。

這里大家比較好奇,為什么會進行這種復雜操作余黎,他的用意是什么重窟?下面來給大家說一下這個過程。

假設 table的大小是16惧财,key1和Key2調用hashCode方法獲取的值的二進制形式分別是:

1111 1111 1111 1101 0000 0000 0000 0001   # key1
1111 1111 1111 1111 0000 0000 0000 0001   # key2

首先我們直接使用key1和key2的hashCode獲取的值去計算在的table的索引值巡扇。
具體過程是:

# key1在table中索引的計算過程與結果
1111 1111 1111 1101 0000 0000 0000 0001  
0000 0000 0000 0000 0000 0000 0000 1111   &    #n-1的二進制
---------------------------------------
0000 0000 0000 0000 0000 0000 0000 0001  # 得到的table索引是1


# key2在table中索引的計算過程與結果
1111 1111 1111 1111 0000 0000 0000 0001  
0000 0000 0000 0000 0000 0000 0000 1111   &   #n-1的二進制
---------------------------------------
0000 0000 0000 0000 0000 0000 0000 0001  #得到的table索引是1

根據上面計算結果可知,雖然key1和key2值不同垮衷,但是最后得到的table的索引都是1厅翔,這樣就會出現了沖突。主要原因是在與n-1進行&操作的時候搀突,通常n的值比較小刀闷,因此高16位都是0,這樣0和任何數&結果都是0描姚。通常key的hashCode取值很不固定涩赢。從最高位到最低位都會出現1的可能。比如key1和key2轩勘,他們的區(qū)別恰恰是出現在自己的hashCode的高16位筒扒,因此key1和key2與n-1進行&操作的結果是一樣的。如果key1和key2經過hash()方法處理后呢绊寻,來看看結果:

# key1在table中索引的計算過程與結果
    1111 1111 1111 1101 0000 0000 0000 0001  #key1本身
^   0000 0000 0000 0000 1111 1111 1111 1101  #key1右移16的值
-----------------------------------------------
    1111 1111 1111 1111 1111 1111 1111 1100      # hash(key1)計算后的值
&   0000 0000 0000 0000 0000 0000 0000 1111      #n-1的二進制
-----------------------------------------------
    0000 0000 0000 0000 0000 0000 0000 1100  #得到的table索引是12



# key2在table中索引的計算過程與結果
    1111 1111 1111 1111 0000 0000 0000 0001   #key2本身
^   0000 0000 0000 0000 1111 1111 1111 1111   #key2右移16的值
-----------------------------------------------
    1111 1111 1111 1111 1111 1111 1111 1110     #hash(key1)計算后的值
&   0000 0000 0000 0000 0000 0000 0000 1111     #n-1的二進制
-----------------------------------------------
    0000 0000 0000 0000 0000 0000 0000 1110 #得到的table索引是14

這樣key1和key2不會出現位置沖突花墩。當key和自己的高16位進行異或操作的后的值的低16位中同時保留了原始key低16位和高16位的特征。因此key1和key2再和n-1進行&運算時澄步,減少了出現相同值的可能性冰蘑。明白了這些內容內容,下一篇文章開始結束HashMap的put和get方法的實現原理村缸。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末祠肥,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子梯皿,更是在濱河造成了極大的恐慌仇箱,老刑警劉巖县恕,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異剂桥,居然都是意外死亡忠烛,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門权逗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來美尸,“玉大人,你說我怎么就攤上這事斟薇∈玻” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵奔垦,是天一觀的道長屹耐。 經常有香客問我,道長椿猎,這世上最難降的妖魔是什么惶岭? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮犯眠,結果婚禮上按灶,老公的妹妹穿的比我還像新娘。我一直安慰自己筐咧,他們只是感情好鸯旁,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著量蕊,像睡著了一般铺罢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上残炮,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天韭赘,我揣著相機與錄音,去河邊找鬼势就。 笑死泉瞻,一個胖子當著我的面吹牛,可吹牛的內容都是我干的苞冯。 我是一名探鬼主播袖牙,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舅锄!你這毒婦竟也來了鞭达?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碉怔,沒想到半個月后烘贴,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡撮胧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了老翘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芹啥。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铺峭,靈堂內的尸體忽然破棺而出墓怀,到底是詐尸還是另有隱情,我是刑警寧澤卫键,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布傀履,位于F島的核電站,受9級特大地震影響莉炉,放射性物質發(fā)生泄漏钓账。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一絮宁、第九天 我趴在偏房一處隱蔽的房頂上張望梆暮。 院中可真熱鬧,春花似錦绍昂、人聲如沸啦粹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唠椭。三九已至,卻和暖如春忍饰,著一層夾襖步出監(jiān)牢的瞬間贪嫂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工喘批, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留撩荣,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓饶深,卻偏偏與公主長得像餐曹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子敌厘,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345