hashmap中直接注意的細節:
紅黑樹長度小於閾值(yu 4聲)6
轉化成兩邊
鍊錶長度大於閾值8 且table的長度大於等於64 才樹化 僅滿足鍊錶長度大於閾值8只會呼叫resize擴容
為什麼是6和8呢,而不設定成一樣呢,因為為了防止在邊界反覆橫跳,浪費效能
if (tab == null || (n = tab.length) < min_treeify_capacity)
resize();
hashmap擴容時資料遷移
a. 舊表槽為空 直接跳過
b. 舊表槽中僅乙個節點 求得新的索引再放入
newtab[e.hash & (newcap - 1)] = e;
c. 舊表中是紅黑樹
紅黑樹中其實依舊維持了鍊錶結構 故處理方法和煉標一樣
d. 舊表中是煉標
首先 e.hash是不變的 這裡設oldcap老表長度為16
則老表的下標計算只是用了e.hash的低4位 已知e.hash&15=j
由於表擴容了一倍 所以現在 e.hash的低5位都應該被使用
而已經有了底4位使用的記錄 如果e.hash的第五位是0 則資料所在位置和老表一樣
如果e.hash的第五位是1 則位置為j+oldcap即j+newtab.length/2
因為如果e.hash的第五位是1 那麼資料必然會儲存在新錶的後半段
而低四位的值就相當於offset這裡是j 就就是儲存位置相對於newtab.length/2的偏差
# 預設初始化容量,在第一次put時table才會初始化為該容量
# 如果new hashmap(n) 指定了長度 則直接table為該容量
static final int default_initial_capacity = 1 << 4; // aka 16
# table最大容量2^30次方
static final int maximum_capacity = 1 << 30;
# 載入因子 當size>default_load_factor*capacity時會擴容
static final float default_load_factor = 0.75f;
# 樹化閾值 鍊錶長度大於閾值8 且table的長度大於64才樹化 僅滿足鍊錶長度大於閾值8只會呼叫resize擴容
static final int treeify_threshold = 8;//大於8 切table長度大於64
# 最小樹化容量 必須同時滿足鍊錶大於8 且長度大於64才能樹化 只滿足大於8就會擴容
static final int min_treeify_capacity = 64;
# 取消樹化閾值 當樹的長度小於6的時候 會退化成煉表
static final int untreeify_threshold = 6;//小於6
# hashmap的雜湊表
transient node table;
# 儲存快取的entryset()。請注意,抽象對映字段用於keyset()和values()。
transient set> entryset;
# 當前大小
transient int size;
# 表結構修改次數 此欄位用於使hashmap集合檢視上的迭代器快速失敗
transient int modcount;
# 下次需要擴容的大小 (capacity * load factor). 當size>threshold且table長度大於64才擴容
int threshold;
# 實際的載入因子 可以在new hashmap(n,loadfactor)的時候指定
final float loadfactor;
public hashmap(int initialcapacity, float loadfactor)
this.loadfactor = loadfactor;
this.threshold = tablesizefor(initialcapacity);
hashcode生成的是32位的碼,為了完整使用到32位,並更好的雜湊,就將高16位與低16位異或(如果相同則為0,如果不同則為1)
【原因:使用 | 會出現更多1 使用 & 會出現更多0 故都不合適】
static
final
inthash
(object key)
這裡我們使用乙個demo來測試一下hash函式,我們可以發現,hash是在hashcode的基礎上,把高16位保持不變,第十六位變成【高十六位和低16位異或】,保證了在不太大的容量的情況下更好的雜湊性
public
static
void
main
(string[
] args)
輸出:
692404036
692393473
101001010001010011111101000100
101001010001010001011000000001
tablesizefor的功能(不考慮大於最大容量的情況)是返回大於輸入引數且最近的2的整數次冪的數。
static
final
inttablesizefor
(int cap)
通過put函式 我們能很好的認識到hashmap的結構
final v putval
(int hash, k key, v value,
boolean onlyifabsent,
boolean evict)
//如果e和當前節點的key相等 則break e記錄舊節點的值
if(e.hash == hash &&
((k = e.key)
== key ||
(key != null && key.
equals
(k))))
break
; p = e;}}
//如果存在舊節點 返回舊值 並更新舊節點的值
if(e != null)
}++modcount;
//大於負載因子*容量的時候才擴容 故初始時put第13個擴容if(
++size > threshold)
//the next size value at which to resize (capacity * load factor).
resize()
;afternodeinsertion
(evict)
;return null;
}
final node
resize()
//newcap=2*oldcap 擴容到原來的兩倍大小
elseif(
(newcap = oldcap <<1)
< maximum_capacity &&
oldcap >= default_initial_capacity)
newthr = oldthr <<1;
// double threshold
}else
if(oldthr >0)
// initial capacity was placed in threshold
//當使用new hashtable(n)給定了長度 n先被轉化成2的冪 然後new node[newcap]出table的長度
newcap = oldthr;
else
//設定下次擴容的閾值capacity*loadfactor
if(newthr ==0)
threshold = newthr;
@suppresswarnings()
node
newtab =
(node
)new
node
[newcap]
; table = newtab;
if(oldtab != null)
else
}while
((e = next)
!= null);if
(lotail != null)
if(hitail != null)}}
}}return newtab;
}
JDK原始碼學習 HashMap
什麼是hash?hash的意思是 雜湊 音譯做 雜湊 輸入乙個任意長度的資料,進過雜湊運算之後,輸出一段固定長度的資料,作為輸入資料的指紋,輸出的結果就是雜湊值。一般來說輸入資料的空間遠遠大於輸出的雜湊值的空間,輸入不同的資料可能會產生相同的雜湊值,所以很難從雜湊值來逆向推出輸入值是什麼。雜湊函式本...
JDK原始碼學習 HashMap(一)
public class hashmapextends abstractmapimplements map,cloneable serializable 類定義 hashmap繼承了abstractmap,實現map,cloneable,serializable 介面 abstractmap 實現了...
JDK原始碼學習之集合
collection介面所有集合類的最頂層介面。從網上找到了乙個uml類圖感覺不錯。接下來會仔細閱讀arraylist,linkedlist,hashset,treeset,甚至更多。先分析這幾個的原因,主要是因為比較常用。1.int size 返回集合中的元素總數 2.boolean isempt...