認識HashMap

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(最大值)時,此時數組將不再擴容

擴容時若某個下標下存在長鏈表,則將長鏈表下數據重新計算並分配在當前下標或當前下標+擴容前數組長度的下標下。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章