HashMap和HashSet的底層實現

HashMap和HashSet是Map接口、Set接口常用的實現類,因此研究這兩個實現原理有助於更好的使用它們,並理解他們的區別。

下面先來分析HashMap的底層實現。

Map中存儲的是<Key,value>對,每個<Key,value>對都以Map.Entry的數據結構存儲,然後用一個table數組來存儲Entry,該table就是根據實例化的參數創建的數組。

存儲的位置則按照Key計算的hash返回值來計算。

以下是Java源代碼HashMap類中添加元素的函數。

    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }

    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

   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);
    }

由此可以看出,當數組的元素已滿時,則double其容量。

然後在bucketindex位置創建一個新的Entry,如果此處已經有一個Entry,則新的Entry指向原來的Entry,產生一個Entry鏈,並且新放入的Entry永遠處於Entry鏈的起端,位於數組bucket中。

當查詢元素時,調用get()函數。

    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;
    }

可見新根據key計算的hash值去找相應的bucket,如果該bucket只有一個元素則直接返回該Value,如果存在一個Entry鏈的話,必須遍歷整個Entry鏈才能找到相應的value值。

由此可見,當table中的每個bucket中都只有一個Entry元素時,該hashMap的性能最好。


明白了HashMap的原理,HashSet就迎刃而解了。因爲HashSet的底層就是用HasHMap來實現的,即把HashMap中的key來存儲元素即實現了set.

我們來看一下HashSet的源碼便知。


    public HashSet() {
        map = new HashMap<>();
    }
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

 




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