通常在我們的應用中,hashmap是用到最多的資料結構之一,在jdk1.8之前,它的底層結構是陣列+鍊錶,而在jdk1.8之後,為了查詢效率的優化(主要是當雜湊碰撞較多的時候),它的底層結構變成了陣列+鍊錶+紅黑樹。今天就來**一下hashmap的擴容機制,這也是面試時被問到最多的問題。首先看一下原始碼中hashmap的四種構造方法。
從原始碼中可以看出:hashmap提供四種構造方法:一是給定初始容量和載入因子的構造方法,二是給定初始容量,使用預設的載入因子,三是什麼引數都不給,使用預設的初始容量和預設的載入因子,四是傳進乙個map,使用預設的載入因子。從上面的構造方法可以看出,無論是使用預設的初始容量,還是使用預設的初始容量,當你呼叫hashmap的構造方法時,hashmap是沒有進行初始化容量,也就是現在是乙個空的hashmap(容量為0),這是因為hashmap使用的懶載入機制,只有你第一次向hashmap中新增元素時,才進行第一次的容量設定,檢視put(k,v)的原始碼:
從上圖中可以看出,put(k key, v value)呼叫了putval(int hash, k key, v value, boolean onlyifabsent,boolean evict)的方法,在putval的方法中,當第一次向hashmap中新增物件時,會進行乙個判空的處理,這時就呼叫resize()方法對hashmap進行容量設定,此時會有兩種情況的根據容量初始化。
第一種情況:當我們沒有設定初始化容量時,hashmap就使用預設的初始化容量,也就是16.
第二種情況:當我們設定了初始化容量,hashmap就會按照我們設定的容量進行設定嗎?答案是不一定。當你設定的初始化容量是2的n次方時,就會按照你設定的容量設定;當你設定的初始化容量不是2的n次方時,就會按照大於你設定的那個值但是最接近你設定的那個值的2的n次方進行設定。聽起來比較拗口,下面從原始碼和例項進行說明。
從hashmap的第乙個構造方法我們可以看出,當我們給定初始容量時,會呼叫tablesizefor(initialcapacity)方法進行容量設定,
這一系列的無符號右移操作和按位或運算返回的什麼結果,我們可以通過乙個測試來說明。測試類如下:
運算結果如下:
結合原始碼和例項,就可以體會到hashmap給定了初始容量時,它本身處理這個值得機制了。
還有就是hashmap什麼時候進行擴容呢?我們回到put()方法的原始碼中,發現有下面一段**:
其中size是hashmap中新增鍵值對的數量,而threshold是容量*載入因子(capacity * load factor)得出的值,也是就當我們的容量是16,載入因子是0.75時,當鍵值對的數量大於16*0.75=12時,hashmap會進行擴容操作,回到resize()方法中,看它是怎麼擴容的?
可以看出,在容量不大於最大值的情況下,hashmap是以2倍的容量進行擴容的。那麼問題又來了,hashmap為什麼會是2倍的形式進行擴容呢?下次再來分享。
通常在我們的應用中,hashmap是用到最多的資料結構之一,在jdk1.8之前,它的底層結構是陣列+鍊錶,而在jdk1.8之後,為了查詢效率的優化(主要是當雜湊碰撞較多的時候),它的底層結構變成了陣列+鍊錶+紅黑樹。今天就來**一下hashmap的擴容機制,這也是面試時被問到最多的問題。首先看一下原始碼中hashmap的四種構造方法。
HashMap的初始容量設定為多少合適?
我們知道對於list map這種底層是基於陣列的動態容器,在擴容時會進行陣列複製,因此為了避免這種額外開銷,應該盡量在建立時指定大小。對於沒有負載因子的如list,其擴容策略為滿了後下次插入進行擴容,因此初始化大小為需要大小即可 對於存在負載因子的hashmap hashset等,其擴容策略為本次插...
HashMap初始容量為什麼是16
這裡不講 只講思路。要稍微懂點二進位制。hashmap的結構是陣列加鍊表,容量指的是陣列長度。元素放入陣列的哪個位置?這由算出來的hashcode 跟 1111 作與運算 得出陣列下標。hashcode具體演算法我也不知道 為什麼是1111,因為0000 1111共16個數,跟陣列下標 容量都一致。...
hashmap 中的初始容量和載入因子之間的聯絡
構造乙個新的空 set,其底層 hashmap 例項具有指定的初始容量和指定的載入因子 public hashset intinitialcapacity,float loadfactor 初始容量 是雜湊表在建立時的容量。載入因子 是雜湊表在其容量自動增加之前可以達到多滿的一種尺度。當雜湊表中的條...