深入扒 HashMap 原始碼
這裡的 jdk 環境是 1.8 會和 1.7 的有區別 一、static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
預設初始化容量,值為 16,例項化 HashMap 時也可以通過 new HashMap<String, Object>(需要的容量); 來指定容量
二、static final int MAXIMUM_CAPACITY = 1 << 30;
預設最大容量,值為 1073741824,是 int 最大值的一半
三、static final float DEFAULT_LOAD_FACTOR = 0.75f;
預設裝載因子,值為 0.75f,HashMap 在例項化時如果沒有指定裝載因子,則會使用這個,但只會在第一次例項化的時候用到,用於計算第一次擴容時需要達到的元素數量,0.75f 意思是指元素數量達到總容量的 75% 時擴容
四、static final int TREEIFY_THRESHOLD = 8;
HashMap 每個桶中的資料由連結串列樹化的閾值,當桶中元素數量超過這個值 並且總容量超過 64(MIN_TREEIFY_CAPACITY)時連結串列會轉化為紅黑樹,否則桶中的元素數量僅僅大於 TREEIFY_THRESHOLD 的話,只會對雜湊表進行 resize() 擴容,至於為什麼要樹化呢?因為當有大量的資料產生 hash 碰撞,原來 jdk 1.7 中用連結串列的形式儲存資料查詢會非常慢,這裡用紅黑樹的形式就快了很多
五、static final int UNTREEIFY_THRESHOLD = 6;
HashMap 桶中元素的數量小於等於 6 的時候,如果原來是樹的話,會退化成連結串列
六、static final int MIN_TREEIFY_CAPACITY = 64;
最小樹化容量,當桶中元素數量超過這個值 並且總容量超過 64(MIN_TREEIFY_CAPACITY)時連結串列會轉化為紅黑樹,否則桶中的元素數量僅僅大於 TREEIFY_THRESHOLD 的話,只會對雜湊表進行 resize() 擴容
七、transient Node<K,V>[] table;
HashMap 內部重要的屬性,用於存放加進來的元素,結構為陣列,陣列中的元素是連結串列或者紅黑樹
八、transient Set<Map.Entry<K,V>> entrySet;
這是 HashMap 內部維護的一個屬性,呼叫 HashMap 的 entrySet() 方法可以獲取到,用來遍歷的話效率會比較高,相當於 HashMap 對外映射出來的一個檢視,比一個個迭代遍歷當然會快
九、transient int size;
當前 HashMap 中元素個數,不是容量哦,外部呼叫 size() 方法時得到的值就是這個
十、transient int modCount;
- 執行 put(K key, V value) 時,也就是新增元素時會自增 1
- 執行 remove(Object key) 或 remove(Object key, Object value) 時,也就是移除元素時會自增 1
- 執行 clear() 時,也就是清除 HashMap 時會自增 1
- 執行 forEach(BiConsumer<? super K, ? super V> action) 時會賦值給一個區域性變數,最後會和這個區域性變數作對比,不相等則拋異常,所以是用來判斷執行緒安全的
- 執行 replaceAll(BiFunction<? super K, ? super V, ? extends V> function) 時同上
- 在幾個內部類中同樣會用到這個變數,同樣的用途都是為了多執行緒操作同一個 HashMap 時的安全性
十一、int threshold;
- 擴容閾值,用於和 HashMap 的容量比較,當 size > threshold 時擴容
- 在預設引數下第一次擴容的時候, threshold = DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY,也就是 threshold = 0.75 * 16 = 12 但是如果以指定容量的方式例項化 HashMap,第一次擴容的時候 threshold = 容量 * loadFactor
- 以後每次擴容都會翻倍
- 最後就是當容量達到 1073741824(MAXIMUM_CAPACITY)時,會擴容到 Integer.MAX_VALUE,也就是 2147483647
- 所以理論上 HashMap 的最大容量是 2147483647
- 要注意的是 threshold 並不會根據 size 的減小而變小,也就是隻會不斷往上漲
十二、final float loadFactor;
- 裝載因子,與擴容相關的引數之一
- 在第一次例項化 HashMap 時,構造方法中有可選引數 loadFactor,不指定的話會預設 loadFactor = DEFAULT_LOAD_FACTOR
- 當容量小於 16(DEFAULT_INITIAL_CAPACITY)或者擴容後大於1073741824(MAXIMUM_CAPACITY)時也會用到,所以並不是每次擴容都會用到
jdk 1.8 中 HashMap 的資料結構