Java 容器系列(七):HashMap 原始碼分析01之建構函式、內部類

cerish404發表於2020-11-18

作為面試必問的 HashMap,是學習集合的重中之重,也是最難的一個。

1. HashMap 的類定義

  • 繼承 抽象map,實現Map 介面,可複製,可克隆
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

1.1 HashMap 的構造方法

  • HashMap 有 4 個構造方法,分別可用於指定載入因子,初始容量,初始化集合
/* 空構造,使用預設載入因子為 0.75  */
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/* 指定 容量大小,使用預設載入因子,呼叫 */
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/* 指定 容量大小,載入因子 */
public HashMap(int initialCapacity, float loadFactor) {
	/* 若初始容量 < 0,跑錯 引數異常 */
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    /* 若指定容量過大,設定為最大容量 MAXIMUM_CAPACITY */                                       
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    /* 若載入因子 < 0 || 傳入的float 是NaN 即(0.0f/0.0f), 報錯引數異常 */    
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    /* 設定載入因子 */
    this.loadFactor = loadFactor;
    /* 初始化容量 */
    this.threshold = tableSizeFor(initialCapacity);
}
/* 初始化一個集合,使用預設載入因子 */
public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

1.2 HashMap 屬性定義

/* 版本號,用於初始化 */
private static final long serialVersionUID = 362498820763181265L;
/* 初始化容量為 16 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
/* 指定最大容量 2^30  */
static final int MAXIMUM_CAPACITY = 1 << 30;
/* 預設載入因子為 0.75 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/* 連結串列變為紅黑樹的閾值 */
static final int TREEIFY_THRESHOLD = 8;
/* 紅黑樹退化連結串列的閾值 */
static final int UNTREEIFY_THRESHOLD = 6;
/* 樹化時容量不小於該值,否則進行擴容 */
static final int MIN_TREEIFY_CAPACITY = 64;
/* 儲存 map 的桶,桶裡面才是連結串列,即 陣列 + 連結串列 */
transient Node<K,V>[] table;
/* 儲存 key,value 的集合 */
transient Set<Map.Entry<K,V>> entrySet;
/* 元素的個數 */
transient int size;
/* 修改次數 */
transient int modCount;
/* 臨界值 當實際大小超過臨界值時,會進行擴容threshold = 載入因子*容量 */
int threshold;
/* 載入因子 */
final float loadFactor;

1.3 HashMap 的內部類

1.1.1 Node

  • 每一個新增的資料都會被封裝成一個 Node 節點,儲存 key,value,key的hash值,與下一個節點的指標
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash; // key 的hash值
    final K key;
    V value;
    Node<K,V> next; // next指標
	/* 全參構造 */
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    public final K getKey()        { return key; } // 獲取 key 值
    public final V getValue()      { return value; } // 獲取 value 值
    public final String toString() { return key + "=" + value; }
	/* 重寫 hashcode 方法 */
    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }
	/* 設定 value 值,返回舊值 */
    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }
	/* 重寫 equals() 方法 */
    public final boolean equals(Object o) {
        if (o == this)
            return true;
        /* 若不是同一個地址值,則需要分別判斷 key,value 是否相等 */    
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

1.1.2 KeySet

  • 儲存 Key 值的集合
final class KeySet extends AbstractSet<K> {
	/* 返回集合個數 */
    public final int size() { return size; }
    /* 清空key,相當於清空 hashMap */
    public final void clear() { HashMap.this.clear(); }
    /* 獲取一個遍歷器 */
    public final Iterator<K> iterator() { return new KeyIterator(); }
    /* 是否包含某個key */
    public final boolean contains(Object o) { return containsKey(o); }
    /* 移除某個key,即移除某個節點 */
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }
    /* 分割迭代器 */
    public final Spliterator<K> spliterator() {
        return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    /* 遍歷每一個key,執行 action 操作 */
    public final void forEach(Consumer<? super K> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            /* 遍歷每一個桶位,在遍歷桶位中的每一個節點 */
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e.key);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}

1.1.3 KeyIterator

  • key值的遍歷迭代器
final class KeyIterator extends HashIterator
    implements Iterator<K> {
    /* 獲取到一個節點,再由節點獲取到 key 值 */
    public final K next() { return nextNode().key; }
}

1.1.4 KeySpliterator

  • Key值的分割遍歷器
static final class KeySpliterator<K,V>
        extends HashMapSpliterator<K,V>
        implements Spliterator<K> {
    /* 構造器:param1: 分割的hashmap, param2:起始位置
    param3: 分割位置, param4:分割個數,param5:期望的修改版本  */    
    KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
                   int expectedModCount) {
        super(m, origin, fence, est, expectedModCount);
    }
	/* 分割方法:二分法擷取分割位置 */
    public KeySpliterator<K,V> trySplit() {
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        /* lo >= mid 即個數太少 || 當前節點為null 不分割,否則採用二分法擷取一半的長度 */
        return (lo >= mid || current != null) ? null :
            new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
                                    expectedModCount);
    }
	/* 遍歷當前遍歷器未遍歷的節點,執行 action 操作 */
    public void forEachRemaining(Consumer<? super K> action) {
        int i, hi, mc;
        if (action == null)
            throw new NullPointerException();
        HashMap<K,V> m = map;
        Node<K,V>[] tab = m.table;
        /* 還未進行過初始化 */
        if ((hi = fence) < 0) {
            mc = expectedModCount = m.modCount;
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        else
            mc = expectedModCount;
        /* 1. tab != null && tab.length >= hi 即 tab 不為空且有元素
         * 2. (i = index) >= 0 即起始位置 >= 0
         * 3. i < (index = hi) || current != null 即 起始位置 < 終點位置 || 當前節點不為空
         */    
        if (tab != null && tab.length >= hi &&
            (i = index) >= 0 && (i < (index = hi) || current != null)) {
            Node<K,V> p = current;
            current = null;
            do {
            	/* p == null 即當前桶位沒有元素了,換下一個 */
                if (p == null)
                    p = tab[i++];
                else {
                    action.accept(p.key);
                    p = p.next;
                }
            } while (p != null || i < hi);
            if (m.modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
	/* 對下一個元素進行 action  操作 */
    public boolean tryAdvance(Consumer<? super K> action) {
        int hi;
        if (action == null)
            throw new NullPointerException();
        /* 獲取 table 陣列 */
        Node<K,V>[] tab = map.table;
        /* 陣列有值(即有桶位) && 桶個數 >= 擷取的長度 && 起始位置 >=0 */
        if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
            /* 當前節點不為空 || 起始位置 < 擷取位置 */
            while (current != null || index < hi) {
            	/* 當前節點為空,換下一個桶 */
                if (current == null)
                    current = tab[index++];
                else {
                	/* 獲取當前key,將current往後移一位,執行 accept 操作,檢查 modCount版本,退出迴圈 */
                    K k = current.key;
                    current = current.next;
                    action.accept(k);
                    if (map.modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    return true;
                }
            }
        }
        return false;
    }
	/* 特徵值: DISTINCT 表示無重複元素、SIZED 表示元素有精確個數 */
    public int characteristics() {
    	/* fence < 0 即該迭代器沒有元素 || est == map.size 為遍歷或分解: 可以確定元素個數  */
        return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
            Spliterator.DISTINCT;
    }
}

1.1.5 Values

  • value 集合
final class Values extends AbstractCollection<V> {
	/* 元素個數 */
    public final int size() { return size; }
    /* 清空 value 即 清空 hashMap */
    public final void clear()  { HashMap.this.clear(); }
    /* 獲取 value 的迭代器 */
    public final Iterator<V> iterator() { return new ValueIterator(); }
    /* 是否包含某一個值 */
    public final boolean contains(Object o) { return containsValue(o); }
    /* 分割迭代器 */
    public final Spliterator<V> spliterator() {
        return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    /* value 值的遍歷,執行 action 操作 */
    public final void forEach(Consumer<? super V> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            /* 遍歷桶位,在遍歷桶位的值 */
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e.value);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}

1.1.6 ValueIterator

  • 繼承 HashIterator,只是重寫了 next 方法
final class ValueIterator extends HashIterator
    implements Iterator<V> {
    /* 獲取下一節點,在獲取 value 值 */
    public final V next() { return nextNode().value; }
}

1.1.7 ValueSpliterator

  • value 值的分割迭代器
static final class ValueSpliterator<K,V>
        extends HashMapSpliterator<K,V>
        implements Spliterator<V> {
    /* 傳入擷取的HashMap,起始位置,終點位置,個數,修改版本,呼叫父類構造即HashMapSpliterator */
    ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est,
                     int expectedModCount) {
        super(m, origin, fence, est, expectedModCount);
    }
	/* 分割方法:二分法 */
    public ValueSpliterator<K,V> trySplit() {
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid || current != null) ? null :
            new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
                                      expectedModCount);
    }
	/* 遍歷執行 action 操作 */
    public void forEachRemaining(Consumer<? super V> action) {
        int i, hi, mc;
        if (action == null)
            throw new NullPointerException();
        HashMap<K,V> m = map;
        Node<K,V>[] tab = m.table;
        if ((hi = fence) < 0) {
            mc = expectedModCount = m.modCount;
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        else
            mc = expectedModCount;
        if (tab != null && tab.length >= hi &&
            (i = index) >= 0 && (i < (index = hi) || current != null)) {
            Node<K,V> p = current;
            current = null;
            do {
                if (p == null)
                    p = tab[i++];
                else {
                    action.accept(p.value);
                    p = p.next;
                }
            } while (p != null || i < hi);
            if (m.modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
	/* 下一個即將遍歷的元素 執行 action 操作  */
    public boolean tryAdvance(Consumer<? super V> action) {
        int hi;
        if (action == null)
            throw new NullPointerException();
        Node<K,V>[] tab = map.table;
        if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
            while (current != null || index < hi) {
                if (current == null)
                    current = tab[index++];
                else {
                    V v = current.value;
                    current = current.next;
                    action.accept(v);
                    if (map.modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    return true;
                }
            }
        }
        return false;
    }
	/* 特徵值: SIZED 表示有精確個數 */
    public int characteristics() {
    	/* fence < 0 即該迭代器沒有元素 || est == map.size 為遍歷或分解: 可以確定元素個數  */
        return (fence < 0 || est == map.size ? Spliterator.SIZED : 0);
    }
}

1.1.8 EntrySet

  • 存放鍵值對的集合,前面的都是放key,或者 value,這裡將二者組合,變成一個 Map.Entry
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
	/* 不管是key,value,還是 EntrySet,在 HashMap 中的個數 size 都是相同的 */
    public final int size() { return size; }
    /* 清空 HashMap */
    public final void clear() { HashMap.this.clear(); }
    /* 獲取鍵值對Map.Entry<K,V> 的遍歷器(其實就是Node,因為Node實現了Map.Entry<K,V>) */
    public final Iterator<Map.Entry<K,V>> iterator() {
        return new EntryIterator();
    }
    /* 是否包含該鍵值對 */
    public final boolean contains(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        /* 強轉型別獲取key,根據key獲取Node,然後在比較 Node 與 e 是否相等 */    
        Map.Entry<?,?> e = (Map.Entry<?,?>) o;
        Object key = e.getKey();
        Node<K,V> candidate = getNode(hash(key), key);
        return candidate != null && candidate.equals(e);
    }
    /* 移除某個物件 */
    public final boolean remove(Object o) {
    	/* 先判斷 o 是否是 Map.Entry 的例項, 獲取 key,value 嘗試刪除節點,成功返回true,否則返回false  */
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Object value = e.getValue();
            return removeNode(hash(key), key, value, true, true) != null;
        }
        return false;
    }
    /* 鍵值對的分割迭代器 */
    public final Spliterator<Map.Entry<K,V>> spliterator() {
        return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    /* 鍵值對遍歷執行 action 操作 */
    public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}

1.1.9 EntryIterator

  • 鍵值對的遍歷器
final class EntryIterator extends HashIterator
    implements Iterator<Map.Entry<K,V>> {
    /* 返回的Node 就是Map.Entry<K,V>  */
    public final Map.Entry<K,V> next() { return nextNode(); }
}

1.1.10 EntrySpliterator

  • 鍵值對(Node)的分割遍歷器
static final class EntrySpliterator<K,V>
        extends HashMapSpliterator<K,V>
        implements Spliterator<Map.Entry<K,V>> {
    /* 跟key、value 一樣,呼叫 HashMapSpliterator */    
    EntrySpliterator(HashMap<K,V> m, int origin, int fence, int est,
                     int expectedModCount) {
        super(m, origin, fence, est, expectedModCount);
    }
	/* 分割方法:二分法 */
    public EntrySpliterator<K,V> trySplit() {
    	/* 個數不多不分割 */
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid || current != null) ? null :
            new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
                                      expectedModCount);
    }
	/* 遍歷 執行 action 操作 */
    public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
        int i, hi, mc;
        if (action == null)
            throw new NullPointerException();
        HashMap<K,V> m = map;
        Node<K,V>[] tab = m.table;
        if ((hi = fence) < 0) {
            mc = expectedModCount = m.modCount;
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        else
            mc = expectedModCount;
        if (tab != null && tab.length >= hi &&
            (i = index) >= 0 && (i < (index = hi) || current != null)) {
            Node<K,V> p = current;
            current = null;
            do {
                if (p == null)
                    p = tab[i++];
                else {
                    action.accept(p);
                    p = p.next;
                }
            } while (p != null || i < hi);
            if (m.modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
	/* 對下一個即將遍歷的元素進行 action 操作 */
    public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
        int hi;
        if (action == null)
            throw new NullPointerException();
        Node<K,V>[] tab = map.table;
        if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
            while (current != null || index < hi) {
                if (current == null)
                    current = tab[index++];
                else {
                    Node<K,V> e = current;
                    current = current.next;
                    action.accept(e);
                    if (map.modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    return true;
                }
            }
        }
        return false;
    }
	/* 特徵值: DISTINCT 表示無重複元素、SIZED 表示元素有精確個數 */
    public int characteristics() {
    	/* fence < 0 即該迭代器沒有元素 || est == map.size 為遍歷或分解: 可以確定元素個數  */
        return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
            Spliterator.DISTINCT;
    }
}

1.1.11 HashIterator

abstract class HashIterator {
    Node<K,V> next;        // 下一個即將返回的節點
    Node<K,V> current;     // 當前節點
    int expectedModCount;  // 期待的修改版本
    int index;             // 當前節點的下標
	/* 空構造:初始化引數 */
    HashIterator() {
        expectedModCount = modCount;
        Node<K,V>[] t = table;
        current = next = null;
        index = 0;
        /* 初始化賦值next */
        if (t != null && size > 0) {
            do {} while (index < t.length && (next = t[index++]) == null);
        }
    }
	/* 是否有下一個元素 */
    public final boolean hasNext() {
        return next != null;
    }
	/* 獲取下一個元素 */
    final Node<K,V> nextNode() {
        Node<K,V>[] t;
        Node<K,V> e = next; // 先儲存返回的 next
        /* 檢查版本 */
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        /* null檢測,這就是為什麼推薦先判斷 hasNext() 的原因,避免報異常 NoSuchElementException */    
        if (e == null)
            throw new NoSuchElementException();
        /* 找到下一個帶返回的節點 */    
        if ((next = (current = e).next) == null && (t = table) != null) {
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }
	/* 移除當前元素,因為有 null 判斷,所以連續兩次呼叫會報異常 IllegalStateException */
    public final void remove() {
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        current = null;
        K key = p.key;
        removeNode(hash(key), key, null, false, false);
        expectedModCount = modCount;
    }
}

1.1.12 HashMapSpliterator

  • hashMap 分割遍歷器
static class HashMapSpliterator<K,V> {
    final HashMap<K,V> map;     // 遍歷的 hashMap
    Node<K,V> current;          // 當前節點
    int index;                  // 當前節點下標
    int fence;                  // 遍歷器的最後一個元素的下標即
    int est;                    // 遍歷器元素的個數
    int expectedModCount;       // 期待的修改版本
	/* 全參構造,操作的HashMap,起始位置,結束位置,元素個數,期望版本 */
    HashMapSpliterator(HashMap<K,V> m, int origin,
                       int fence, int est,
                       int expectedModCount) {
        this.map = m;
        this.index = origin;
        this.fence = fence;
        this.est = est;
        this.expectedModCount = expectedModCount;
    }
	/* 獲取分割位置 */
    final int getFence() { // initialize fence and size on first use
        int hi;
        /* 若 fence < 0,則當前屬性還未賦值 */
        if ((hi = fence) < 0) {
            HashMap<K,V> m = map;
            est = m.size;
            expectedModCount = m.modCount;
            Node<K,V>[] tab = m.table;
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        return hi;
    }
	/* 返回未遍歷的元素個數,先強制呼叫getFence(); 強制初始化引數 */
    public final long estimateSize() {
        getFence(); // force init
        return (long) est;
    }
}

1.1.13 總結

  • HashMap 採用鍵值對(key,value) 的方式存資訊,底層使用 Node 節點儲存元素資訊,陣列 + 連結串列/紅黑樹 的方式儲存多個元素值。
  • HashMap 提供了三種迭代器分別對 HashMap 的 key值、value值與Entry鍵值對進行遍歷,並且提供對應的分解迭代器。

相關文章