HashMap深度解析(一)
阿新 • • 發佈:2019-02-16
本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/16843543,轉載請註明。
HashMap可以說是Java中最常用的集合類框架之一,是Java語言中非常典型的資料結構,我們總會在不經意間用到它,很大程度上方便了我們日常開發。在很多Java的筆試題中也會被問到,最常見的,“HashMap和HashTable有什麼區別?”,這也不是三言兩語能說清楚的,這種筆試題就是考察你來筆試之前有沒有複習功課,隨便來個快餐式的複習就能給出簡單的答案。
HashMap計劃寫兩篇文章,一篇是HashMap工作原理,也就是本文,另一篇是多執行緒下的HashMap會引發的問題。這一年文章寫的有點少,工作上很忙,自己業餘時間也做點東西,就把部落格的時間佔用了,以前是力保一週一篇文章,有點給自己任務的意思,搞的自己很累,文章質量也不高,有時候寫技術文章也是需要靈感的,為了舉一個例子可能要絞盡腦汁,為了一段程式碼可能要驗證好多次,現在想通了,有靈感再寫,需要一定的積累,才能把自己瞭解的知識點總結歸納成文章。
言歸正傳,瞭解HashMap之前,我們需要知道Object類的兩個方法hashCode和equals,我們先來看一下這兩個方法的預設實現:
- /** JNI,呼叫底層其它語言實現 */
- publicnativeint hashCode();
- /** 默認同==,直接比較物件 */
- publicboolean equals(Object obj) {
- return (this == obj);
- }
-
publicboolean
- if (this == anObject) {
- returntrue;
- }
- if (anObject instanceof String) {
- String anotherString = (String) anObject;
- int n = value.length;
- if (n == anotherString.value.length) {
- char v1[] = value;
-
char
- int i = 0;
- // 逐個判斷字元是否相等
- while (n-- != 0) {
- if (v1[i] != v2[i])
- returnfalse;
- i++;
- }
- returntrue;
- }
- }
- returnfalse;
- }
- 自反性:對於任何非空引用值 x,x.equals(x) 都應返回 true。
- 對稱性:對於任何非空引用值 x 和 y,當且僅當 y.equals(x) 返回 true 時,x.equals(y) 才應返回 true。
- 傳遞性:對於任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,並且 y.equals(z) 返回 true,那麼 x.equals(z) 應返回 true。
- 一致性:對於任何非空引用值 x 和 y,多次呼叫 x.equals(y) 始終返回 true 或始終返回 false,前提是物件上 equals 比較中所用的資訊沒有被修改。
- 對於任何非空引用值 x,x.equals(null) 都應返回 false。
- 在 Java 應用程式執行期間,在同一物件上多次呼叫 hashCode 方法時,必須一致地返回相同的整數,前提是物件上 equals 比較中所用的資訊沒有被修改。從某一應用程式的一次執行到同一應用程式的另一次執行,該整數無需保持一致。
- 如果根據 equals(Object) 方法,兩個物件是相等的,那麼在兩個物件中的每個物件上呼叫 hashCode 方法都必須生成相同的整數結果。
- 以下情況不 是必需的:如果根據 equals(java.lang.Object) 方法,兩個物件不相等,那麼在兩個物件中的任一物件上呼叫 hashCode 方法必定會生成不同的整數結果。但是,程式設計師應該知道,為不相等的物件生成不同整數結果可以提高雜湊表的效能。
現在我們知道,執行put方法後,最終HashMap的儲存結構會有這三種情況,情形3是最少發生的,雜湊碼發生碰撞屬於小概率事件。到目前為止,我們瞭解了兩件事:
- HashMap通過鍵的hashCode來快速的存取元素。
- 當不同的物件hashCode發生碰撞時,HashMap通過單鏈表來解決,將新元素加入連結串列表頭,通過next指向原有的元素。單鏈表在Java中的實現就是物件的引用(複合)。
- public V put(K key, V value) {
- // 處理key為null,HashMap允許key和value為null
- if (key == null)
- return putForNullKey(value);
- // 得到key的雜湊碼
- int hash = hash(key);
- // 通過雜湊碼計算出bucketIndex
- int i = indexFor(hash, table.length);
- // 取出bucketIndex位置上的元素,並迴圈單鏈表,判斷key是否已存在
- for (Entry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- // 雜湊碼相同並且物件相同時
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- // 新值替換舊值,並返回舊值
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- // key不存在時,加入新元素
- modCount++;
- addEntry(hash, key, value, i);
- returnnull;
- }