HashMap是我們日常開發中經常用到的一種數據類型,一直沒有好好的瞭解一下它,昨天聽了大佬的課,略有收穫,記下再說。
HashMap在jdk1.7及以前,他的數據結構爲數組加鏈表,jdk1.8及之後,數據結構爲數組+鏈表+紅黑樹。
在HashMap存儲數據時,通過調用hash(k)的方法計算k的hash值,然後結合數組長度,計算數組下標。
static final int hash(Object key) {
int h;
//高16位異或低16位,避免高位不參與下標的計算引起hash衝突
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
當k的hash值在hashMap中不存在,則執行插入。
當k的hash值存在時,判斷兩者本身是否值相等,相等則數據覆蓋,不等則插入到同一下標。
ps:1.7時爲頭插法,1.8後爲尾插法
1.8後,當同一下標的鏈表長度爲8時,此時單向鏈表進化爲紅黑樹,當執行刪除操作,樹的非空節點爲6時,退化爲單向鏈表。
選擇紅黑樹的原因是爲了解決二叉查找樹的缺陷,二叉查找樹在特殊情況下會變成一條線性結構,等同於鏈表,遍歷查詢效率極低。 而紅黑樹在插入數據或者刪除數據後,會通過左旋、右旋、變色的操作來保持樹的平衡,提高查詢效率。
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
選擇8的原因:
理想情況下,在隨機哈希代碼下,桶中的節點頻率遵循
泊松分佈,文中給出了桶長度k的頻率表。
由頻率表可以看出,桶的長度超過8的概率非常非常小。所以作者應該是根據
概率統計而選擇了8作爲閥值。
*
* 0: 0.60653066
* 1: 0.30326533
* 2: 0.07581633
* 3: 0.01263606
* 4: 0.00157952
* 5: 0.00015795
* 6: 0.00001316
* 7: 0.00000094
* 8: 0.00000006
* more: less than 1 in ten million
*
hashMap默認擴容閾值爲0.75,當數組中的數據量達到了數組長度的0.75倍時,數組進行擴容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int MAXIMUM_CAPACITY = 1 << 30;
擴容時、調用resize()方法將數組擴容爲原來的2倍,注意,當數組的長度達到2^30(最大值)時,此時數組將不再擴容
擴容時若某個下標下存在長鏈表,則將長鏈表下數據重新計算並分配在當前下標或當前下標+擴容前數組長度的下標下。