1. 程式人生 > 其它 >淺談Map

淺談Map

技術標籤:學習筆記

Map介面

雙列集合,用來儲存一對(key - value)一對的資料

Map介面實現類體系結構

在這裡插入圖片描述

HashMap

雜湊表(hash table)

相當於是一個書架,其實是陣列+連結串列

HashMap底層

陣列+連結串列 (JDK7及之前),陣列+連結串列+紅黑樹(JDK8)

HashMap原始碼分析

HashMap中的幾個屬性
① DEFAULT_INITIAL_CAPACITY : HashMap的預設容量,16
② DEFAULT_LOAD_FACTOR:HashMap的預設載入因子:0.75
③ threshold:擴容的臨界值 = 容量*填充因子:16 * 0.75 = 12,呼叫HashMap的空參構造器, 預設擴容的臨界值為12

④ TREEIFY_THRESHOLD:Bucket中連結串列長度大於該預設值,轉化為紅黑樹:8
⑤ MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64

原始碼
在這裡插入圖片描述

HashMap map = new HashMap(); 在例項化以後,首次呼叫put()底層建立了長度是16的以為陣列Entry[] table
補充
JDK8相較於JDK7在底層實現方面不同:
① new HashMap():底層沒有建立一個長度為16的陣列
② JDK8底層的陣列是Node[],而非Entry[]
③ 首次呼叫put()時,底層建立長度為16的陣列

V put(K key, V value)

判斷hashtable是否存在,如果不存在則建立

if ((tab = table) == null || (n = tab.length) == 0)
	n = (tab = resize()).length;

判斷當前陣列下標對應的元素是否為null,如果是則建立一個Node物件放入

if ((p = tab[i = (n - 1) & hash]) == null)
	tab[i] = newNode(hash, key, value, null);

如果傳入的key在雜湊表中存在的話,進行資料的替換

if (p.hash == hash &&
	((k = p.
key) == key || (key != null && key.equals(k)))) e = p;

判斷當前節點是否是紅黑樹

else if (p instanceof TreeNode)

如果連結串列的長度大於8並且陣列長度大於64時,會將連結串列轉成紅黑樹
補充
因為binCount從0開始,所有binCount >= 7即連結串列長度大於8時進入treeifyBin(tab, hash)

for (int binCount = 0; ; ++binCount) {//binCount記錄雜湊桶中連結串列的長度
	if ((e = p.next) == null) {
	    p.next = newNode(hash, key, value, null);
	    if (binCount >= TREEIFY_THRESHOLD - 1) //TREEIFY_THRESHOLD = 8,
	        treeifyBin(tab, hash);
	    break;
	}
	...程式碼省略

在treeifyBin(tab, hash)中雜湊桶的長度小於64就擴容,並不是連結串列長度大於8就變成紅黑樹
所以連結串列的長度大於8並且陣列長度大於64時,才會將連結串列轉成紅黑樹

if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)//MIN_TREEIFY_CAPACITY = 64
	resize();	

為什麼初始長度是2的n次冪

為了保證根據hash計算出索引位置的正確性,底層使用的是 & 位於運算計算出資料在雜湊桶的索引位置
在這裡插入圖片描述
HashMap map = new HashMap(17);
當手動設定Hash Map初始長度不是2的n次冪,呼叫tableSizeFor(int cap)計算出比傳入引數(cap)大的且離他最近的2的n次冪的值,並將該值賦給threshold

    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

例如:
如果傳入17,則返回32,如果傳入13,會返回16

結語

筆者處於學習階段,並沒有過多深入探究原始碼(主要是看不懂),該文章如果哪裡有論述不是很清楚的或者是寫錯的還望大佬們糾正,萬分感謝。