Java中HashMap實現原理淺析

概述

HashMap是基於Map接口的實現。這個實現提供了所有可選的映射操作,並且允許null值和null鍵。(除了不同步和允許空值以外,HashMap和Hashtable幾乎等價)。這個類不保證映射之間的順序;尤其是,它不保證隨着時間的推移映射順序不變(翻譯自Java API文檔)。


哈希表是一個“鏈表散列”的結構,是由數組和鏈表結合而成。

HashMap的基層是一個數組結構,而數組中的每一項又可以拓展成爲一個鏈表。具體的實現形式Entry就是數組中的元素,它是由key和value組成的基本單元,其本身持有Entry的引用來構成鏈表形式。


基本方法實現

此處源碼版本爲JDK1.7。

put方法

    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        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;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

其實現的過程如下:

首先檢測key值,並由此得出它在數組中的index值,判斷是否已有key存在,若已在則覆蓋;若不存在則將新元素加到鏈表後,加元素操作具體由addEntry方法實現。


在addEntry方法中包含有擴容判斷和createEntry方法,createEntry方法將新創建的 Entry 放入 bucketIndex 索引處,並讓這個新的 Entry 指向原來的 Entry。


get方法

    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }


    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

get方法中用getEntry來判斷key值在原數組中是否已存在並做取值操作。


總體來說,HashMap的底層是用Entry數組來實現的,通過key的哈希函數來尋找指定的key-value對位置並進行操作。


HashMap的refresh操作

當HashMap中的元素個數超過數組大小乘以負載因子時,就需要對HashMap進行擴容,類似的操作在List中也有。

    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }


新建Entry數組後將原數組中的元素轉移到新數組中,即重新計算它們在新數組中的位置。默認的負載因子是0.75,擴容規模爲原來的2倍。


以上爲對HashMap實現的基本理解,更詳細的內容參考http://zhangshixi.iteye.com/blog/672697


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章