我之見--HashMap原理解析

第一:HashMap的概念 

<span style="font-size:14px;">HashMap 基於哈希表的 Map 接口的實現。此實現提供所有可選的映射操作,並允許使用 null 值和 null 鍵。(除了非同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證映射的順序,特別是它不保證該順序恆久不變。</span>

第二:什麼是哈希表?

<span style="font-size:14px;"> 散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度,這個映射函數叫做散列函數,存放記錄的數組叫做散列表 PS:給定表M,存在函數f(key),對任意給定的關鍵字值key,代入函數後若能得到包含該關鍵字的記錄在表中的地址,則稱表M爲哈希(Hash)表,函數f(key)爲哈希(Hash) 函數。</span>
<span style="font-size:14px;">
</span>

第三:什麼是HashCode? 

<span style="font-size:18px;">hashCode是jdk根據對象的地址或者字符串或者數字算出來的int類型的數值。</span>
<span style="font-size:14px;">
</span>
java Hash Map 就是根據對象的hashCode的值,然後通過散列函數計算出存放的地址(也就是索引值)。但是如果遇到計算出來的值相同的話,這種情況叫做哈希衝突,ps:y = x % H  ,如果 x = object.hashCode(); 則y 計算出來的值 就可以能相同,當然出現社種衝突的時候,我們一般可以有以下方法解決。

  1. 開放地址法
    開放地執法有一個公式:Hi=(H(key)+di) MOD m i=1,2,...,k(k<=m-1)
    其中,m爲哈希表的表長。di 是產生衝突的時候的增量序列。如果di值可能爲1,2,3,...m-1,稱線性探
    測再散列。

    如果di取1,則每次衝突之後,向後移動1個位置.如果di取值可能爲1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k(k<=m/2) 
    稱二次探測再散列。如果di取值可能爲僞隨機數列。稱僞隨機探測再散列
  2. 再哈希法 
    當發生衝突時,使用第二個、第三個、哈希函數計算地址,直到無衝突時。缺點:計算時間增加。
    比如上面第一次按照姓首字母進行哈希,如果產生衝突可以按照姓字母首字母第二位進行哈希,再衝突,第三位,直到不衝突爲止
  3. 鏈地址法
    將所有關鍵字爲同義詞的記錄存儲在同一線性鏈表中。
  4. 建立一個公共溢出區
    假設哈希函數的值域爲[0,m-1],則設向量HashTable[0..m-1]爲基本表,另外設立存儲空間向量OverTable[0..v]用以存儲發生衝突的記錄。
    經過以上方法,基本可以解決掉hash算法衝突的問題。

第四:HashMap源碼學習

HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable
因此HashMap是實現Map接口的,同時 實現Serializable接口,所以可以序列化。每個HashMap內部都有一個HashMapEntry 數級對象。
 
我們來看一下 HashMap的 put方法
 @Override public V put(K key, V value) {
        if (key == null) {
            return putValueForNullKey(value);
        }

        int hash = secondaryHash(key.hashCode());
        HashMapEntry<K, V>[] tab = table;
        int index = hash & (tab.length - 1);
        for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }

        // No entry for (non-null) key is present; create one
        modCount++;
        if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
        }
        addNewEntry(key, value, hash, index);
        return null;
    }
如果Key值 爲空,則加入putValueForNullKey(value);

先計算hash 值 
 int hash = secondaryHash(key.hashCode()); 然後 根據 hash值計算index值 :
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
遍歷數組查找是否有已經相同key存在,則更新原來的值。否則添加到數組中。addNewEntry(key, value, hash, index)
我們再來看一下 get方法 :
    public V get(Object key) {
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            return e == null ? null : e.value;
        }

        // Doug Lea's supplemental secondaryHash function (inlined)
        int hash = key.hashCode();
        hash ^= (hash >>> 20) ^ (hash >>> 12);
        hash ^= (hash >>> 7) ^ (hash >>> 4);

        HashMapEntry<K, V>[] tab = table;
        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                e != null; e = e.next) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return e.value;
            }
        }
        return null;
    }
根本是根據hashCode計算index (hash & (tab.length - 1).取相應的Entry,然後對比key值,返回value







。 。
 

發佈了43 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章