Java集合的擴容機制

在面試後臺開發的過程中,集合是面試的熱話題,不僅要知道各集合的的區別用法,還要知道集合的擴容機制,今天我們就來談下ArrayList 和 HashMap的默認大小以及擴容機制。
在Java8中,查看源碼可以知道:ArrayList 的默認大小是10個元素,HashMap 的默認大小是16個元素。
在這裏插入圖片描述
在這裏插入圖片描述
ArrayList、Vector默認初始容量爲10

Vector:線程安全,但速度慢
底層數據結構是數組結構
加載因子爲1:即當 元素個數 超過 容量長度時,進行擴容
擴容增量:原容量的1倍
eg:Vector的容量爲10,一次擴容後時容量爲20

ArrayList:線程不安全,查詢速度快
底層數據結構是數組結構
擴展增量:原容量的 0.5倍+1
eg:ArrayList的容量爲10,一次擴容後是容量爲16

Set(集)元素無序的、不可重複。

HashSet:線程不安全,存取速度快
底層實現是一個HashMap(保存數據),實現Set接口
默認初始容量爲16
加載因子爲0.75:即當 元素個數 超過 容量長度的0.75倍時,進行擴容
擴容增量:原容量的1倍
eg:HashSet的容量爲16,一次擴容後爲32

Map是一個雙列集合

HashMap:默認初始量爲16
(爲何是16:16是2^4,可以提高查詢效率,另外,32=16<<1)

加載因子爲0.75:即當 元素個數 超過 容量長度的0.75倍 時,進行擴容

擴容增量:原容量的 1 倍

如 HashSet的容量爲16,一次擴容後是容量爲32

HashMap 的默認大小是16個元素,必須是2的冪,爲什麼呢?

hashMap的數組長度一定保持2的次冪,比如16的二進制表示爲 10000,那麼length-1就是15,二進制爲01111,同理擴容後的數組長度爲32,二進制表示爲100000,length-1爲31,二進制表示爲011111。

這樣會保證低位全爲1,而擴容後只有一位差異,也就是多出了最左位的1,這樣在通過 h&(length-1)的時候,只要h對應的最左邊的那一個差異位爲0,就能保證得到的新的數組索引和老數組索引一致(大大減少了

之前已經散列良好的老數組的數據位置重新調換),還有,數組長度保持2的次冪,length-1的低位都爲1,會使得獲得的數組索引index更加均勻。

  1. static int indexFor(int h, int length) {
  2.       return h & (length-1);  
    
  3. }
    首先算得key得hashcode值,然後跟數組的長度-1做一次“與”運算(&)。看上去很簡單,其實比較有玄機。比如數組的長度是2的4次方,那麼hashcode就會和2的4次方-1做“與”運算。很多人都有這個疑問,

爲什麼hashmap的數組初始化大小都是2的次方大小時,hashmap的效率最高,我以2的4次方舉例,來解釋一下爲什麼數組大小爲2的冪時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相同的機率較小,那麼數據在數組上分佈就比較均勻,也就是說碰撞的機率小,相對的,查詢的時候就不用遍歷某個位置上的鏈表,這樣查詢效率也就較高了。

說到這裏,我們再回頭看一下hashmap中默認的數組大小是多少,查看源代碼可以得知是16,爲什麼是16,而不是15,也不是20呢,看到上面的解釋之後我們就清楚了吧,顯然是因爲16是2的整數次冪的原因,

在小數據量的情況下16比15和20更能減少key之間的碰撞,而加快查詢的效率。

啥也不是,什麼東西

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章