1. 程式人生 > >HashMap的初始大小為什麼是2的n次冪?

HashMap的初始大小為什麼是2的n次冪?

下面是HashMap的一個建構函式,兩個引數initialCapacity,loadFactor

這關係HashMap的迭代效能。

image.png

 

關於這兩個引數值的設定界限:

1. initialCapacity是map的初始化容量,initialCapacity > MAXIMUM_CAPACITY,表明map的最大容量是1<<30,也就是1左移30位,每左移一位乘以2,所以就是1*2^30=1073741824.

2. loadFactor是map的負載因子,loadFactor <= 0 || Float.isNaN(loadFactor),表明負載因子要大於0,且是非無窮大的數字

 

負載因子為什麼會影響HashMap效能

首先回憶HashMap的資料結構,我們都知道有序陣列儲存資料,對資料的索引效率都很高,但是插入和刪除就會有效能瓶頸(回憶ArrayList),連結串列儲存資料,要一次比較元素來檢索出資料,所以索引效率低,但是插入和刪除效率高(回憶LinkedList),兩者取長補短就產生了雜湊雜湊這種儲存方式,也就是HashMap的儲存邏輯.而負載因子表示一個散列表的空間的使用程度,有這樣一個公式:initailCapacity*loadFactor=HashMap的容量。

所以負載因子越大則散列表的裝填程度越高,也就是能容納更多的元素,元素多了,連結串列大了,所以此時索引效率就會降低。反之,負載因子越小則連結串列中的資料量就越稀疏,此時會對空間造成爛費,但是此時索引效率高。

 

如何科學設定 initailCapacity,loadFactor的值

HashMap有三個建構函式,可以選用無參建構函式,不進行設定。預設值分別是16和0.75.

官方的建議是initailCapacity設定成2的n次冪,laodFactor根據業務需求,如果迭代效能不是很重要,可以設定大一下。

 

為什麼initailCapacity要設定成2的n次冪:深入理解HashMap

左邊兩組是陣列長度為16(2的4次方),右邊兩組是陣列長度為15。兩組的hashcode均為8和9,但是很明顯,當它們和1110“與”的時候,產生了相同的結果,也就是說它們會定位到陣列中的同一個位置上去,這就產生了碰撞,8和9會被放到同一個連結串列上,那麼查詢的時候就需要遍歷這個連結串列,得到8或者9,這樣就降低了查詢的效率。同時,我們也可以發現,當陣列長度為15的時候,hashcode的值會與14(1110)進行“與”,那麼最後一位永遠是0,而0001,0011,0101,1001,1011,0111,1101這幾個位置永遠都不能存放元素了,空間浪費相當大,更糟的是這種情況中,陣列可以使用的位置比陣列長度小了很多,這意味著進一步增加了碰撞的機率,減慢了查詢的效率!

所以說,當陣列長度為2的n次冪的時候,不同的key算得得index相同的機率較小,那麼資料在陣列上分佈就比較均勻,也就是說碰撞的機率小,相對的,查詢的時候就不用

遍歷某個位置上的連結串列,這樣查詢效率也就較高了。

 

resize()方法

initailCapacity,loadFactor會影響到HashMap擴容。

HashMap每次put操作是都會檢查一遍 size(當前容量)>initailCapacity*loadFactor 是否成立。如果不成立則HashMap擴容為以前的兩倍(陣列擴成兩倍),然後重新計算每個元素在陣列中的位置,然後再進行儲存。這是一個十分消耗效能的操作。所以如果能根據業務預估出HashMap的容量,應該在建立的時候指定容量,那麼可以避免resize().

PS:更多技術講解請關注360linker官方公眾號,並有資格加入社群免費獲取IT技術視訊教程,讓你快速精煉漲知識漲技術