HashMap原理簡單認識
1. hashMap簡單認識
優點:hashMap的儲存方式是鍵值對(鍵可以包括null),查詢速度,儲存方便,儲存數量最大為十幾億。
缺點:主要是執行緒不安全,容易在hashmap擴容時形成死迴圈;
2.hashMap從原始碼角度簡單認識
jdk1.7 使用的陣列 + 連結串列
jdk1.8 使用的陣列 + 連結串列 + 紅黑樹
分析為什麼使用陣列 + 連結串列 + 紅黑樹:
陣列:主要是方便查詢,且在記憶體中是連續的。
連結串列:主要是方便插入,刪除,然而hashMap把兩個者有點結合在一起。
紅黑樹:主要是jdk1.8為了對連結串列進行優化時,增加的資料結構。那麼在它們如何使用的呢? ,可以從put()
loadFactor | 負載因子,預設值75% |
threshold | 臨界值,超過臨界值需要重新分配 |
modCount | 統計刪除和修改的次數 |
Entry | 實體類,主要有四個引數組成,分別是key,hash,value,next |
hashmap的建構函式
1.無參構造
2.有參構造
分析hashmap的建構函式:
通過在平時專案觀察發現,大部分人都會使用無參的建構函式,因為會有初始值16,那麼問題來了,如果我們儲存數量大於16 呢,看過原始碼的人當然會說,需要進行擴容操作,可是資料量比較大的時候是不是申請很多記憶體空間,把一個數組裡面的資料重新放到另一個數組裡面去,這樣可能比較消耗時間和記憶體,如果在多執行緒情況下,是不是容易出現數據問題,我個人觀點,提前指定所需要的空間,那麼有人會說,我可以知道需要申請多少空間,如果盲目申請記憶體是不是也會產生浪費,這樣的觀點是沒有錯的,但是我這裡強調的是,在你大概知道需要多少資料量,應指定所需要申請的空間。
hashmap裡面的put操作
addEntry函式:
createEntry:
putForNullKey函式:
hashMap裡面put方法:
1. 首先初始化陣列
2. 判斷鍵是不是null,如果是null,用putForNullKey()來處理
3. 計算出雜湊值
4. 用雜湊值和陣列長度進行indexFor()運算;得到下標i;
5. 通過下標i在數組裡面找到對應的實體類,比較實體類裡面hash值和key的==和equals方法,都相等的話,說明是要找的value
否則話,進行增加實體類
6. 在增加實體類時,都會進行判斷,是否超過threshold值,如果超過就把原來陣列的大小變為2倍。
分析put方法:
可以從原始碼和上述過程中,可以看出,在進行put操作時,首先會進行雜湊值進行比較,然後在比較key的==和equal方法,那麼在什麼時候使用equal方法呢?,其實涉及到一個雜湊衝突的問題,就是hashcode可能存在衝突,最後會和buckIndex的值一樣,在這一種情況下,會呼叫equal方法,因為equal是Object方法,它直接比較的是記憶體大小。
hashmap裡面的put操作通過hashcode值可以存放資料。當不同物件的雜湊值相同時,會通過單鏈表的方式解決,將新元素插入表頭,它的next指向原來的元素。(為什麼插入表頭不插入表尾,正在研究)
分析put方法的執行緒不安全:
hashmap很容易在put操作時,形成死迴圈。
分析死迴圈形成的原因:
1. 從上述原始碼中,可以看出死迴圈的原因主要是因為在進行擴容操作所造成的。
2. 首先把原來的大小變為2倍。
3. 遍歷原來的陣列
4. 因為是單鏈表,所以要儲存下一個節點, Entry<k,v> next = e.next;
5. 因為e要插入表頭,並且需要e指向連結串列的第一個元素,所以需要把e.next = newTable[i];
6. 然後讓e變為陣列的頭指標。newTable[i] = e;
其實上述過程實際是單向連結串列的反轉,在形成死迴圈時,主要是在第4 ,5 ,6步。
3.hashmap所涉及到的資料結構
1. 連結串列
2. 雜湊運算
3. 紅黑樹(jdk1.8自己正在研究中)