在面試後臺開發的過程當中,集合是面試的熱話題,不只要知道各集合的區別用法,還要知道集合的擴容機制,今天咱們就來談下ArrayList 和 HashMap的默認大小以及擴容機制。java
在 Java 7 中,查看源碼能夠知道:ArrayList 的默認大小是 10 個元素,HashMap 的默認大小是16個元素(必須是2的冪,爲何呢???下文有解釋)。這就是 Java 7 中 ArrayList 和 HashMap 類 的代碼片斷:面試
// from ArrayList.java JDK 1.7 private static final int DEFAULT_CAPACITY = 10; //from HashMap.java JDK 7 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
這裏要討論這些經常使用的默認初始容量和擴容的緣由是:數組
當底層實現涉及到擴容時,容器或從新分配一段更大的連續內存(若是是離散分配則不須要從新分配,離散分配都是插入新元素時動態分配內存),要將容器原來的數據所有複製到新的內存上,安全
這無疑使效率大大下降。加載因子的係數小於等於1,意指即當元素個數超過容量長度*加載因子的係數時,進行擴容。另外,擴容也是有默認的倍數的,不一樣的容器擴容狀況不一樣。數據結構
List 元素是有序的、可重複性能
ArrayList、Vector默認初始容量爲10spa
Vector:線程安全,但速度慢線程
底層數據結構是數組結構code
加載因子爲1:即當 元素個數 超過 容量長度 時,進行擴容blog
擴容增量:原容量的 1倍
如 Vector的容量爲10,一次擴容後是容量爲20
ArrayList:線程不安全,查詢速度快
底層數據結構是數組結構
擴容增量:原容量的 0.5倍+1
如 ArrayList的容量爲10,一次擴容後是容量爲16
Set(集) 元素無序的、不可重複。
HashSet:線程不安全,存取速度快
底層實現是一個HashMap(保存數據),實現Set接口
默認初始容量爲16(爲什麼是16,見下方對HashMap的描述)
加載因子爲0.75:即當 元素個數 超過 容量長度的0.75倍 時,進行擴容
擴容增量:原容量的 1 倍
如 HashSet的容量爲16,一次擴容後是容量爲32
Map是一個雙列集合
HashMap:默認初始容量爲16
(爲什麼是16:16是2^4,能夠提升查詢效率,另外,32=16<<1)
加載因子爲0.75:即當 元素個數 超過 容量長度的0.75倍 時,進行擴容
擴容增量:原容量的 1 倍
如 HashSet的容量爲16,一次擴容後是容量爲32
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之間的碰撞,而加快查詢的效率。