对HashMap数据结构的理解——加载因子和初始容量
先看源碼:
解釋一下位移運(yùn)算:
1<<4 是位移運(yùn)算的表示,為十進(jìn)制16
1的二進(jìn)制表示:1
左移4位之后的二進(jìn)制表示為B(10000) = D(16)
更簡單的計算方法就是 1<< n 等效于 1 乘以 2的 n 次方
進(jìn)入正題
HashMap底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表,JDK1.8中還引入了紅黑樹,當(dāng)鏈表長度超過8個時,會將鏈表轉(zhuǎn)成紅黑樹,以提升其查找性能。
HashMap有兩個參數(shù)影響其性能:初始容量和加載因子。
1、HashMap的初始容量
容量是哈希表中桶的數(shù)量,初始容量只是哈希表在創(chuàng)建時的容量。
2、HashMap的加載因子
加載因子是哈希表在其容量自動擴(kuò)容之前可以達(dá)到多滿的一種度量。
3、作用
當(dāng)哈希表中的條目數(shù)超出了加載因子與當(dāng)前容量的乘積時,則要對該哈希表進(jìn)行擴(kuò)容、rehash操作(即重建內(nèi)部數(shù)據(jù)結(jié)構(gòu)),擴(kuò)容后的哈希表容量為原來的兩倍。
為了減少沖突的概率,當(dāng)HashMap的數(shù)組長度到了一個臨界值就會觸發(fā)擴(kuò)容,把所有元素rehash再放到擴(kuò)容后的容器中,所以說rehash是一個非常耗時的操作。
而這個臨界值是由加載因子和當(dāng)前容器的容量大小來確定:
DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR
即默認(rèn)情況下是 16x0.75 =12 時,就會觸發(fā)擴(kuò)容操作。
4、面試高頻:為什么加載因子初始化是0.75呢?
- 也是一個綜合考慮,如果設(shè)置過小如0.5,HashMap 每 put 少量的數(shù)據(jù),都要進(jìn)行一次擴(kuò)容,而擴(kuò)容操作會消耗大量的性能。使得空間利用率很低,同時提高了rehash(重建內(nèi)部數(shù)據(jù)結(jié)構(gòu))操作的次數(shù)。
- 如果設(shè)置過大的話,比如設(shè)成1,容量還是16,假設(shè)現(xiàn)在數(shù)組上已經(jīng)占用了15個,再要put數(shù)據(jù)進(jìn)來,計算數(shù)組 index 時,發(fā)生 hash碰撞 的概率將達(dá)到15/16,這違背了 HashMap 減少 hash碰撞 的原則。同時,這樣會減少空間開銷,提高空間利用率,但同時會增加查詢時間的成本。
- 因此,選擇0.75作為默認(rèn)的加載因子,完全是時間和空間成本上尋求折中的選擇。
- 在設(shè)置初始容量時應(yīng)該考慮到映射中所需的條目數(shù)及其加載因子,以便最大限度地減少rehash操作次數(shù),所以,一般在使用HashMap時建議根據(jù)預(yù)估值設(shè)置初始容量,減少擴(kuò)容操作。
4、面試高頻:為什么初始容量是16
當(dāng)容量為2的冪次方時,源碼中 n -1 對應(yīng)的二進(jìn)制數(shù)全為1,這樣才能保證它和 key 的 hashcode 做&運(yùn)算后,能夠均勻分布,這樣才能減少hash碰撞的次數(shù)。至于默認(rèn)值為什么是16,而不是2 、4、8,或者32、64、1024等,應(yīng)該就是個折中處理,過小會導(dǎo)致放不下幾個元素,就要進(jìn)行擴(kuò)容了,而擴(kuò)容是一個很消耗性能的操作。取值過大的話,無疑會浪費(fèi)更多的內(nèi)存空間。因此在日常開發(fā)中,如果可以預(yù)估HashMap會存入節(jié)點(diǎn)的數(shù)量,則應(yīng)該在初始化時,指定其容量。
參考原文:
HashMap容量和負(fù)載因子:https://blog.csdn.net/ye17186/article/details/88876417
HashMap中的初始容量和加載因子:https://blog.csdn.net/weixin_44723496/article/details/112387738
總結(jié)
以上是生活随笔為你收集整理的对HashMap数据结构的理解——加载因子和初始容量的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 八皇后问题和八数码问题的最陡上升爬山法、
- 下一篇: 深入了解EntityFramework—