面試常客HashMap中index的計算細節(jié)

背景

文章不聊HashMap的源碼實現(xiàn),主要聊一聊幾個細節(jié)的問題。

HashMap處理沖突的方式是通過鏈表法來解決沖突,其核心在于散列函數(shù)的均勻隨機。因此我們就來看一看HashMap是怎樣計算index的。

源碼很直接:

// capacity等于HashMap的length
int index = hash(key) & (capacity - 1)

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

上述代碼就是HashMap計算index的方式。那么這里展開兩個問題:

  1. hash()之后的值,為什么要& (capacity - 1)
  2. hash()這個函數(shù)是為了什么?

問題1:

首先,我們需要得到一個小于length的index。一個比較簡單的方式就是對當前的length進行取余,也就是hash(key) % (capacity)

這里有一個優(yōu)化,那就是把%換成&。其原理就是:a%(2^n) == a&(2^n-1)。

這也是為什么length需要是2的倍數(shù)的原因

因此也就變成了hash(key) & (capacity - 1)

補充:為什么a%(2^n) == a&(2^n-1)

通過一個例子來看一下:18 % 16 = 2

image
image
  • 大于16的1肯定是16的整數(shù)倍,可以直接約掉
  • 剩下&出來的值就是余數(shù)

問題2:

hash()函數(shù)一共做了兩件事:

  1. 將hashCode右移16位
  2. 將第一步的結(jié)果與hashCode進行一個異或

為什么做這兩件事,咱們來展開一下。先看一種case,假設(shè)我們有一個對象,hashCode為下圖:

image

這個hashCode有個特點:有幾位都是0

如果我們直接用這個HashCode與(capacity - 1)進行與(&)操作,如果與的HashCode低位都是0,那么無論(capacity - 1)是什么,得到的值永遠是0。

因此會出現(xiàn):當滿足這種case,無論怎么計算index都是0。一個穩(wěn)定復現(xiàn)的沖突。

所以我們需要考慮如何解決這種情況。這里我們套用HashMap的方案“也就是右移,然后異或。

這里就會發(fā)現(xiàn)得到的index的確不是0了:

image

其原理也比較好理解:

先右移16位,那么原HashCode的低16位就變成了高的16位,高16位全部變成0。

將右移后的結(jié)果與原HashCode進行異或,得到的結(jié)果:高16位還是原樣,低16位進行了混合。因此之前為0的低位,變得不一定為0了。

套用HashMap的方案,我們的確解決了低位為0的情況

那么問題來了,如果高位是0怎么辦?

怎么辦?不需要辦呀...因為我們通過&求index,先從低位計算。 如果我們index需要計算高位,那就意味著我們的HashMap已經(jīng)非常非常巨大了...

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

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

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