先認識幾個預設常量:
/*** the load factor used when none specified in constructor.
* 預設的負載因子0.75
*/static final floatdefault_load_factor= 0.75f;
/*** the default initial capacity - must be a power of two.
* 預設初始容量 16
*/static final intdefault_initial_capacity= 1 << 4; // aka 16
/**建構函式有四個,下面分別簡單說一下:* the maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* must be a power of two <= 1<<30.
* ,最大容量,容量取值範圍[2,230]
*/static final intmaximum_capacity= 1 << 30;
// 預設建構函式,全都採用預設值
public
hashmap()
// 指定「容量大小」的建構函式
public
hashmap
(int initialcapacity)
// 指定「容量大小」和「載入因子」的建構函式
public
hashmap
(int initialcapacity,
float loadfactor)
public
hashmap
(map<
?extendsk,
?extends
v> m)
size是實際存放資料的量(兩個hash值相同的資料,會計數兩次)首先要知道,key到index的對映是怎麼實現的:// 臨界值 當實際大小(容量*填充因子)超過臨界值時,會進行擴容 intthreshold;
hash=key.hashcode();
index = (length - 1) & hash;
//length為陣列的長度
從key獲取到key存放的陣列的下標index,是先取key的hash值,然後將hash值和陣列長度減一進行了與運算(而不是傳統的取模運算%)
這是為了提高對映的效率。每次存、取、擴容操作都會涉及大量的key對映inde運算,特別是每次擴容,曾經存好得到資料又要全部重新計算index。
好了前面說完了現在來說為什麼陣列大小要是二次冪。
「取餘(%)操作中如果除數是2的冪次則等價於與其除數減一的與(&)操作(也就是說 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。
這裡又涉及了一些預定義好的常量
/**
* 將鍊錶轉化二叉樹的閾值 8
*/static
final
int treeify_threshold =8;
/** * 將二叉樹轉化回鍊錶的閾值 6
*/static
final
int untreeify_threshold =6;
/** * 將鍊錶轉化成二叉樹時,最小容量值 64(注意:不是 map 中的元素個數)
*/static
final
int min_treeify_capacity =
64;
為什麼轉換閾值設為8呢?
hashmap節點分布遵循泊松分布,按照泊松分布的計算公式計算出了鍊錶中元素個數和衝突概率的對照表,可以看到鍊錶中元素個數為8時的概率已經非常小。
下一章開始詳細學習一下put和get方法
HashMap元素插入和擴容
從jdk1.8開始hashmap的儲存結構變成了陣列 鍊錶 紅黑樹,單鏈表中元素個數超過指定閾值,會轉化為紅黑樹結構儲存 提高查詢效率 從1.7到1.8,在hash衝突的時候,鍊錶的插入將頭插法改為尾插法,防止在高併發的情緒出現迴圈鍊錶 hashmap的預設陣列大小為16,代表hash陣列的長度 預...
hashmap 的擴容和樹形化
鍊錶轉紅黑樹的閾值 static final int treeify threshold 8 紅黑樹轉鍊錶的閾值 static final int untreeify threshold 6 最小樹形化容量閾值 即 當雜湊表中的容量 該值時,才允許樹形化煉表 即 將鍊錶 轉換成紅黑樹 否則,若桶內元...
HashMap的擴容機制 為什麼是2冪
假設length為hash表陣列的大小,方法indexfor int hash,int length 為 indexfor int hash,int length 在舊陣列中同一條entry鏈上的元素,在resize過程中,通過重新計算索引位置後,有可能被放到了新陣列的不同位置上。jdk8做了一些優...