阿里巴巴開發規范中,推薦用戶在初始化HashMap時,應指定集合初始值大小。
一、原因
這個不用多想,肯定是效率問題,那為什么會造成效率問題呢?
當我們new一個HashMap沒有對其容量進行初始化的時候,系統會默認創建一個16大小的集合。當我們使用的集合太小時,就會造成內存的浪費,而當HashMap的容量超過臨界值時,HashMap就會擴容到下一個2的指數冪(2->4,4->8,8->16)。擴容(resize)時,HashMap會重新建立hash表,重新計算沒個元素的位置,這是很消耗資源的。
二、合理的設置
當我們使用HashMap(int initialCapacity)來初始化容量的時候,jdk會默認幫我們計算出一個相對合理的值當做初始容量。當HashMap的容量值超過了臨界值(threshold)時就會擴容,threshold = HashMap的容量值*0.75,比如初始化容量為8的HashMap當大小達到8*0.75=6時將會擴容到16。當我們設置HashMap的初始化容量是遵循expectedSize(預期大小)/0.75+1,比如expectedSize是6時 6/0.75+1=9,此時jdk處理后會被設置成16,大大降低了HashMap被擴容的幾率。
當我們通過HashMap(int initialCapacity)設置初始容量時,HashMap不一定會采取我們設置的初始容量,會根據計算得到一個新的值(2的指數冪),以保證hash的效率。
PS.為什么容量一定要是2的指數冪?
簡答:1.節約空間 2.讓元素分布均勻
詳細:
hashMap源碼獲取元素的位置:
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
return h & (length-1);
}
h:為插入元素的hashCode
length:為map長度
&:與操作
如果length為2的次冪 則length-1 轉化為二進制必定是11111……的形式,再與h的二進制與操作效率會非常的快,而且空間不浪費;如果length不是2的次冪,比如length為15,則length-1為14,對應的二進制為1110,再與h與操作,最后一位都為0,而0001,0011,0101,1001,1011,0111,1101這幾個位置永遠都不能存放元素了,空間浪費相當大,更糟的是這種情況中,數組可以使用的位置比數組長度小了很多,這意味着進一步增加了碰撞的幾率,減慢了查詢的效率!這樣就會造成空間的浪費。