注:玩的是JDK1.7版本html
一:仍是原來的風格,先上一下類的繼承關係圖,這樣可以比較清楚的知道此類的相關特性java
二:HashMap.java 的代碼比較難看,因此,我看了幾天,寫的話也分開來寫,這樣能表達的更清晰,HashMap.java 的底層數據結構,本質是單向鏈表數組,以下所示是單向鏈中節點的結構信息數組
三:既然 HashMap.java 的底層數據結構是單向鏈表數組,那麼咱們即可以想象一下數組和單向鏈表這兩種數據結構的特色,而後再回頭想一想 HashMap.java 的實現,而後再看源碼就更容易理解了,以下所示是可能的結構樣子。安全
1)一般應該是以下所示的結構形式,哈希值比較均勻,部分存在衝突數據結構
2)極端狀況多是以下所示的結構形式,存在大量衝突,單向鏈表數組 變成了 單向鏈表app
3)極端狀況多是以下所示的結構形式,沒有任何衝突,單向鏈表數組 變成了 簡單的數組性能
四:看完如上 HashMap.java 的底層數據結構的可能呈現的樣子以後,咱們再看一下 HashMap.java 中的有關屬性,我的感受可能的結構瞭解後,更容易理解這些屬性的本質,注意:HashMap.java 的特色是可動態擴容哈!測試
1)HashMap的默認初始化容量(16),表示HashMap當前最多可以裝載16個元素,注意:必須是2的冪次方this
/** * The default initial capacity - MUST be a power of two. */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
2)HashMap的最大容量 2的30次方=1073741824url
/** * The maximum capacity, used if a higher value is implicitly specified * by either of the constructors with arguments. * MUST be a power of two <= 1<<30. */ static final int MAXIMUM_CAPACITY = 1 << 30;
3)HashMap默認的裝載因子(0.75f),用於衡量HashMap滿的程度,0.75=3/4,換言之當HashMap中的元素超過當前容量的3/4的時候,HashMap就會進行動態的擴容
/** * The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f;
4)HashMap沒有真正放置元素時,是一個空數組
/** * An empty table instance to share when the table is not inflated. */ static final Entry<?,?>[] EMPTY_TABLE = {};
5)HashMap沒有真正放置元素時,是一個空數組,注意:HashMap的容量長度必須是2的冪次方
/** * The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
6)HashMap中 key-value mapping 映射對的個數
/** * The number of key-value mappings contained in this map. */ transient int size;
7)HashMap 動態擴容的臨界值,每當 size>threshold 的時候,HashMap 就會動態擴容了,threshold = capacity * load factor
/** * The next size value at which to resize (capacity * load factor). * @serial */ // If table == EMPTY_TABLE then this is the initial capacity at which the // table will be created when inflated. int threshold;
8)裝載因子,用於 HashMap 是否進行動態擴容計算的變量之一,默認值是0.75f,如無必要一般沒必要改變
/** * The load factor for the hash table. * * @serial */ final float loadFactor;
五:實驗實驗,玩一把,看看什麼狀況
1)結論性信息的都放在了代碼註釋之中,以下所示(能夠本身動手調整一下參數配置,跑跑看)
/** * @description:玩一把HashMap * @author:godtrue * @create:2018-09-28 */ public class TestMap { /** * 開始循環的基數 */ public static final int START_LOOP=1; /** * 結束循環的基數 */ public static final int END_LOOP=17; /** * *@description: 測試入口,主方法 *@param args *@return: void *@author: godtrue *@createTime: 2018-09-28 9:53 *@version: v1.0 */ public static void main(String[] args) { /** * 此處能夠調用不一樣的構造方法來觀察,HashMap 的容量、裝載因子、擴容臨界值、K-V映射對的個數等重點參數之間的關係 * 重點強調一次 * 1:capacity 容量——HashMap最多能裝載元素個數 * 2:loadFactor 裝載因子——表示HashMap滿的程度,判斷HashMap是否擴容的變量之一 * 3:threshold 擴容臨界值——判斷HashMap是否擴容的標準( threshold = capacity * loadFactor ) * 4:size HashMap 已經裝載的元素個數——已經轉載進入HashMap的 K-V mapping 映射對的個數 * * 5:HashMap 可以動態擴容,當 size > threshold 時 HashMap,便會自動庫容,每次擴容的長度是原來 容量 的 2 倍 * 6:HashMap 的 key 和 value 均可覺得 null * 7:HashMap 是非線程安全的 * */ //Map hashMap = new HashMap<String,String>(); Map hashMap = new HashMap<String,String>(1); for(int i = TestMap.START_LOOP;i<TestMap.END_LOOP;i++){ /** * 此處能夠控制 key 值,來觀察一下運行的狀況 */ //hashMap.put(null,"i am godtrue"+i); //hashMap.put("godtrue","i am godtrue"+i); hashMap.put("godtrue"+i,"i am godtrue"+i); printMapInfo(hashMap,i); } System.out.println("hashMap is : "+hashMap); } /** * *@description: 將 Map 的參數信息打印到控制檯,主要是打印 容量、裝載因子、擴容臨界值、K-V映射對的個數 等參數信息 *@param map *@param i *@return: void *@author: godtrue *@createTime: 2018-09-28 *@version: v1.0 */ private synchronized static void printMapInfo(Map map,int i){ System.out.println("添加第 "+i +" 個元素後"); printMapMethodInfo(map,"capacity"); printMapFieldInfo(map,"loadFactor"); printMapFieldInfo(map,"threshold"); printMapFieldInfo(map,"size"); System.out.println("***********************************************\n"); } /** * *@description: 將 Map 的屬性信息打印到控制檯,主要是打印 裝載因子、擴容臨界值、K-V映射對的個數 等參數信息 *@param map *@param property *@return: void *@author: godtrue *@createTime: 2018-09-28 *@version: v1.0 */ private static void printMapFieldInfo(Map map,String property){ try { Class<?> mapType = map.getClass(); Field field = mapType.getDeclaredField(property); field.setAccessible(true); System.out.println(field +" : "+ field.get(map)); }catch (Exception e){ System.err.println("e is :"+e); e.printStackTrace(); } } /** * *@description: 將 Map 的方法信息打印到控制檯,主要是想打印 容量 的信息 *@param map *@param property *@return: void *@author: godtrue *@createTime: 2018-09-28 *@version: v1.0 */ private static void printMapMethodInfo(Map map,String property){ try { Class<?> mapType = map.getClass(); Method method = mapType.getDeclaredMethod(property); method.setAccessible(true); System.out.println(method +" : "+ method.invoke(map)); }catch (Exception e){ System.err.println("e is :"+e); e.printStackTrace(); } } }
2)仔細觀察以下日誌,能夠印證上述代碼註釋中的部分結論,注意:請重點關注 capacity、loadFactor、threahold、size之間的變化關係
添加第 1 個元素後 int java.util.HashMap.capacity() : 1 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 0 transient int java.util.HashMap.size : 1 *********************************************** 添加第 2 個元素後 int java.util.HashMap.capacity() : 2 //擴容 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 1 transient int java.util.HashMap.size : 2 *********************************************** 添加第 3 個元素後 int java.util.HashMap.capacity() : 4 //擴容 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 3 transient int java.util.HashMap.size : 3 *********************************************** 添加第 4 個元素後 int java.util.HashMap.capacity() : 4 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 3 transient int java.util.HashMap.size : 4 *********************************************** 添加第 5 個元素後 int java.util.HashMap.capacity() : 8 //擴容 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 6 transient int java.util.HashMap.size : 5 *********************************************** 添加第 6 個元素後 int java.util.HashMap.capacity() : 8 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 6 transient int java.util.HashMap.size : 6 *********************************************** 添加第 7 個元素後 int java.util.HashMap.capacity() : 8 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 6 transient int java.util.HashMap.size : 7 *********************************************** 添加第 8 個元素後 int java.util.HashMap.capacity() : 8 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 6 transient int java.util.HashMap.size : 8 *********************************************** 添加第 9 個元素後 int java.util.HashMap.capacity() : 16 //擴容 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 12 transient int java.util.HashMap.size : 9 *********************************************** 添加第 10 個元素後 int java.util.HashMap.capacity() : 16 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 12 transient int java.util.HashMap.size : 10 *********************************************** 添加第 11 個元素後 int java.util.HashMap.capacity() : 16 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 12 transient int java.util.HashMap.size : 11 *********************************************** 添加第 12 個元素後 int java.util.HashMap.capacity() : 16 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 12 transient int java.util.HashMap.size : 12 *********************************************** 添加第 13 個元素後 int java.util.HashMap.capacity() : 32 //擴容 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 24 transient int java.util.HashMap.size : 13 *********************************************** 添加第 14 個元素後 int java.util.HashMap.capacity() : 32 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 24 transient int java.util.HashMap.size : 14 *********************************************** 添加第 15 個元素後 int java.util.HashMap.capacity() : 32 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 24 transient int java.util.HashMap.size : 15 *********************************************** 添加第 16 個元素後 int java.util.HashMap.capacity() : 32 final float java.util.HashMap.loadFactor : 0.75 int java.util.HashMap.threshold : 24 transient int java.util.HashMap.size : 16 *********************************************** hashMap is : {godtrue4=i am godtrue4, godtrue5=i am godtrue5, godtrue2=i am godtrue2, godtrue3=i am godtrue3, godtrue8=i am godtrue8, godtrue9=i am godtrue9, godtrue6=i am godtrue6, godtrue7=i am godtrue7, godtrue1=i am godtrue1, godtrue10=i am godtrue10, godtrue12=i am godtrue12, godtrue11=i am godtrue11, godtrue14=i am godtrue14, godtrue13=i am godtrue13, godtrue16=i am godtrue16, godtrue15=i am godtrue15} Process finished with exit code 0
六:幾個爲何?
1)爲何 HashMap 的默認容量是 16, 而且強調容量必須是 2 的冪次方呢?
HashMap 的容量必須是 2 的冪次方,主要是出於性能的考慮,能夠使用 位於運算 來計算單向鏈表數組的下標位置。
詳情可參考
http://www.hollischuang.com/archives/2091
http://www.cnblogs.com/chenssy/p/3521565.html
https://blog.csdn.net/justloveyou_/article/details/62893086
默認值爲何是16呢?
首先,16 是 2的4次方,符合容量是 2 的冪次方的強性規定,其次,我猜想 16 多是一個樣本比較集中的 HashMap的容量
2)爲何 HashMap 的默認裝載因子是 0.75f,而且不建議自定義呢?
HashMap 的默認裝載因子是0.75f,主要是時間和空間成本上一種折衷。
詳情可參考
http://alex09.iteye.com/blog/539545/
3)爲何 HashMap 在擴容的時候,老是擴大原來容量的 2 倍呢?
首先,容量擴大 2 倍後,仍然符合容量是 2 的冪次方的強性規定(注意:容量是2的冪次方),其次,一樣是出於性能考慮,直接經過左移移位即可實現
七)本篇,編寫的過程當中參看了 http://www.hollischuang.com/archives/2416