更新時間:2022-05-17 10:14:56 來源:動力節點 瀏覽2461次
在jdk1.7中,擴容需要滿足以下兩個條件:
1.存儲新值時已有元素的個數必須大于等于閾值
2.當前存儲數據發生在新值存儲hash時碰撞
在jdk1.8中,擴容只需要滿足一個條件:當前存儲新值時已有元素的個數大于等于閾值(注意不替換已有元素)(已有元素等于閾值,下一個存儲必然會觸發擴容機制)或者將數據保存到鏈表中。這時候如果數據大于8,總數小于64,就會發生擴容。注意:
(1)擴展必須在放置新值的時候,新值不在替換之前位置的情況下
(2)擴展發生后,會判斷存儲的對象個數。如果大于閾值,將執行擴展。
(3)第一次put的時候,先觸發擴容(實例化的HashMap的內部數組默認為null,即不實例化。第一次調用put方法時,會先啟動初始化和擴展,長度為16.),然后保存數據,然后判斷是否需要擴展;如果不是第一次,則不會再次初始化。直接保存數據,然后判斷是否需要擴容;
HashMap的容量有一個上限,必須小于1<<30,即1073741824。如果容量超過這個數,就不再增加,閾值設置為Integer.MAX_VALUE([公式] ,即永遠不會超過閾值)。
源碼:jdk7中new Hashmap()時初始化對象,而jdk8中new Hashmap()并沒有初始化對象,而是在put()方法中通過判斷對象是否為空,如果為Null初始化通過調用 resize() 對象。
public V put ( K key , V value ) {
return putVal ( hash ( key ) , key , value , false , true ) ;
}
/**
* 實現 Map.put 及相關方法
*
* @param 哈希鍵值計算傳遞下標
* @參數鍵
* @參數值
* @param onlyIfAbsent true 只在值為空時存儲數據,false 存儲數據
* @param 驅逐
* @return 返回覆蓋的值,如果沒有覆蓋則返回 null
*/
final V putVal ( int hash , K key , V value , boolean onlyIfAbsent , boolean evict ) {
//聲明入口數組 object tab[]: current Entry[] object
Node < K , V > [ ] tab ;
//聲明入口對象p:這里表示存儲的單個節點
Node < K , V > p ;
//n:為當前Entry對象的長度//i:為下標
int n , i為當前存放對象節點的位置;
/**
* 過程判斷
* 1.如果當前Node數組(tab)為空,直接創建(通過resize()創建),創建后設置當前長度為n
* 2.如果要存儲對象的Node節點為空,直接在對象存儲位置新建Node,直接存儲值
* 3.存儲的Node數組不為空,存儲的下標節點Node不為空(Node節點為鏈表的第一個節點)
* 1) 比較存儲在鏈表第一個節點的對象和當前對象是否為同一個對象,如果是則覆蓋并返回原值
* 2) 如果不分兩種情況
* (1)存儲節點為紅黑樹節點結構,調用putTreeVal()方法直接插入數據
* (2)如果不是紅黑樹,則表示為鏈表,遍歷
* A.如果存儲的鏈表的下一個位置為空,先直接存儲該值,保存后再檢查當前存儲的位置是否大于鏈表的第8位
* 一種。如果大于,則調用treeifyBin方法判斷是否對鏈表進行擴展或轉換為紅黑樹(大于8且總數據量大于64,則轉紅黑,否則為數組將擴大)
* b。當前保存的位置鏈表長度不大于8,則保存成功,終端循環操作。
* B. 如果鏈表中存儲的下一個位置有值,且該值與存儲的對象“相同”,則直接覆蓋,返回原值
* 以上兩種情況AB執行后,判斷返回的原始對象是否為空,如果不是,則返回原始對象的原始值
* 上述123三種情況中,如果原值沒有被覆蓋,則表示新增了新存儲的數據。數據存儲后,size+1,然后判斷當前數據量是否大于閾值。
* 如果大于閾值,則擴容。
*
/ if ( ( tab = table ) == null || ( n = tab.length ) == 0 )
n = ( tab = resize ( ) ) 。 _ 長度;if ( ( p = tab [ i = ( n - 1 ) & hash ] ) == null
)
tab [ i ] = newNode ( hash , key , value , null ) ;
否則 {
節點< K , V > e ; ?? ;
if ( p . hash == hash &&
( ( k = p . key ) == key || ( key != null&&鍵等于( . k ) ) ) )
e = p ;
else if ( p instanceof TreeNode )
//直接將數據存入紅黑樹
e = ( ( TreeNode < K , V > ) p ) . putTreeVal (這,選項卡,哈希,鍵,值);
else {
for ( int binCount = 0 ; ; ++ ; binCount ) {
if ( ( e = p . next ) == null ) {
p . next = newNode (哈希,鍵,值, null ) ;
如果 ( binCount >= TREEIFY_THRESHOLD - 1 ) //-1 for 1st
treeifyBin ( tab , hash ) //該方法判斷是擴展還是需要將鏈表轉換為紅黑樹break ;}
if ( e . Hash == hash &&
( ( k = e . Key ) == key || ( key ! = null && key . Equals ( k ) ) ) )
;
p = e ;
}
}
if ( e != null ) { //key
V oldValue = e的現有映射。價值;if ( ! onlyIfAbsent || oldValue == null )
e . 價值=價值;afterNodeAccess ( e ) ; 返回舊值;} }
++ modCount ;
//如果不是替換數據保存,而是新位置保存后,地圖大小加1,然后判斷容量是否超過閾值,如果超過則擴容
if ( ++ size > threshold )
調整大小( ) ;
afterNodeInsertion (驅逐) ;
返回空值;
}
treeifyBin() 方法判斷是否將當前鏈表展開或轉換為紅黑樹
/**
* 替換給定哈希索引處 bin 中的所有鏈接節點,除非
* 表太小,在這種情況下調整大小。
* 從鏈表中指定哈希位置的節點開始,全部替換為紅黑樹結構。
* 除非整個數組對象(Map集合)的數據量非常小(小于64),這種情況下Map是通過resize()來擴展的,而不是把鏈表轉化為紅黑樹。
*/
final void treeifyBin ( HashMap . Node < K , V > [ ] tab , int hash ) {
int n , index ; 哈希映射。節點< K , V > e ;
//如果Map為空或者當前存儲數據n(可以理解為map的size())個數小于64,然后進行擴容
if ( tab == null|| ( n = tab.Length ) < MIN_TREEIFY_CAPACITY )調整大小( ) ; _ //如果size()大于64,則將存儲值的鏈表轉換為紅黑樹else if ( ( e = tab [ index = ( n - 1 ) & hash ] ) != null ) {
哈希映射。樹節點< K ,
V > hd = null , tl = null ;
做 {
哈希映射。TreeNode < K , V > p = replacementTreeNode ( e , null ) ;
如果 ( tl == null )
hd = p ;
否則 {
p 。上一頁= tl ;
tl. 下一個= p ;
}
tl = p ;
} while ( ( e = e . next ) != null ) ;
如果 ((標簽[索引] =高清) !=空)
高清。樹化(選項卡);
}
}
Java 源代碼執行此操作:
先根據key計算hashCode值,然后對數組長度減一做AND運算。為什么當 hashmap 數組的初始大小是 2 的冪時,hashmap 效率最高?你可以看到第七個問題。因此,當數組長度為2的n次冪時,不同key計算的索引相同的概率較小,則數據在數組上分布更均勻,查詢效率更高.
HashMap中默認的數組大小為16,因為16是2的整數次冪,在數據量較小的情況下可以更好的減少key之間的沖突。存儲大容量時,最好預先指定HashMap的大小為2的整數次冪。如果不指定,HashMap的構造方法也會初始化為大于和最接近指定的2次冪價值。
HashMap展開時:必須重新計算原數組中的數組,放入新數組中。這是調整大小。那么我們什么時候應該擴張呢?當 HashMap 中的元素個數超過數組大小 * loadFactor(加載因子)時,數組會被擴展。loadFactor 的默認值為 0.75。
所以一開始,在指定HashMap的大小時,我們把它設置為最接近我們預期的數/0.75,得到最接近并大于他的數(這個數是2的指數倍數),所以我們同時考慮 & 的問題,也避免了調整大小的問題。
如果大家對此比較感興趣,想了解更多相關知識,可以關注一下動力節點的HashMap擴容機制解讀,里面有更豐富的知識等著大家去學習,希望對大家能夠有所幫助哦。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習