1. 程式人生 > >深入分析HashMap的底層實現

深入分析HashMap的底層實現

底層實現

    當向 HashMap 中 put 一對鍵值時:

        它會根據 key 的 hashCode 的hash演算法值計算出一個位置, 該位置就是此物件準備往陣列中存放的位置。 如果該位置沒有物件存在,就將此物件直接放進陣列當中;如果該位置已經有物件 存在了,則順著此存在的物件的鏈開始尋找(Entry 類有一個 Entry 型別的 next 成員 變數,指向了該物件的下一個物件), 如果此鏈上有物件的話,再去使用 equals 方 法進行比較(比較傳入的key和Entry上的key),如果equals 方法比較為 false,則將該物件放到 陣列當中,然後將陣列中該位置以前存在的那個物件連結到此物件的後面。 這也就是一個物件為什麼重寫了hashCode之後還要重寫equals方法的原因。

  public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        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;
    }
當向 HashMap 中 get時:

它會根據 key 的 hashCode 的hash演算法值計算出一個位置,然後會把這個位置的Entry鏈拿到,去迴圈這個鏈中的Entry物件,比較get引數的key和Entry物件中的Key是否相等,如果相等直接返回,反之不返回

public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        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.equals(k)))
                return e.value;
        }
        return null;
    }

擴容機制

hashMap中陣列的初始長度是16,負載因子o.75;也就是說當hashmap中陣列被填充數超過16*0.75個之後就會擴充。擴充為長度的兩倍。

 threshold = (int)(capacity * loadFactor);
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }
如果平時使用時知道放入元素的大小,可以手動指定長度,因為長度擴容是把原來的元素重新複製一遍:
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);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }
比較耗費效能