刁難問題,為什麼HashMap默認容量為16加載因子為0.75

前言:實際開發中我們大多數都是隻能new HashMap<>來存儲鍵值對,很少會去設置初始容量,雖然我們知道他的默認容量是16。但是在面試中,為了體現你個人好學的能力,還是會被經常問到為什麼默認容量是16,hashMap的底層瞭解嗎這類問題。說下小編最近碰見的面試題。

面試官:HashMap的容量為什麼是16?

我:設置16是因為是2的冪,符合內部計算的機制,而且這個值,不大也不小,太小了就有可能頻繁發生擴容,影響效率。太大了又浪費空間。而加載因子0.75的是為了提高空間利用率和減少查詢成本的折中,0.75的話碰撞最小。

這裡解釋下為什麼這麼說。看過HashMap源碼的都應該知道存儲數據要經過hash(key)和indexFor(h,length)來確定存儲在哪個位置

刁難問題,為什麼HashMap默認容量為16加載因子為0.75

hashMap源碼

那麼return h & (length-1) 是什麼意思呢?其實,他就是取模。Java之所有使用位運算(&)來代替取模運算(%),最主要的考慮就是效率。因為位運算直接對內存數據進行操作,不需要轉成十進制,所以位運算要比取模運算的效率更高。

換算過來就是這樣的公式:X % 2^n = X & (2^n – 1)

可能大家還是看不懂,我寫一個例子:

刁難問題,為什麼HashMap默認容量為16加載因子為0.75

取模例子

所以這裡出現一個重要點就是隻要保證length的長度是2^n 的話,就可以實現取模運算。也就是我上面提到的容量必須滿足2的冪。這裡就會有人提出不是可以自定義擴容嗎,那我就不設置成2的冪,怎麼招呢,其實人家也不傻,早都想好怎麼對付你了。從源碼中可以看得出,他不一定會採用你設置的,你設置的不合理,他會自己計算出一個合理的值。比如我設置為5

刁難問題,為什麼HashMap默認容量為16加載因子為0.75

取出容量大小

可以看得出容量變為8,如果設置成9,就會變成16。這裡有一個規律,就是可以把不合理的數轉化成第一個比他自身大的2的冪的數,比如5只有2^3=8,才是第一個比5大的,所以就設置為8。

在HashMap中,threshold = loadFactor * capacity。

loadFactor是加載因子,表示HashMap滿的程度,默認值為0.75f。對於一個默認的HashMap來說,當其size大於12(16*0.75)時就會觸發擴容。為什麼說0.75是折中選擇呢?加載因子過高,例如為1,雖然減少了空間開銷,提高了空間利用率,但同時也增加了查詢時間成本;加載因子過低,例如0.5,雖然可以減少查詢時間成本,但是空間利用率很低,同時提高了rehash操作的次數。所以,一般在使用HashMap時建議根據預估值設置初始容量,減少擴容操作。選擇0.75作為默認的加載因子,完全是時間和空間成本上尋求的一種折中選擇。


分享到:


相關文章: