HashMap 1.7工做原理

HashMap 數組

hashmap本質數據加鏈表。根據key取得hash值,而後計算出數組下標,若是多個key對應到同一個下標,就用鏈表串起來,新插入的在前面。ide

************************再強調一點,兩個String的hashCode相同並不表明着equals比較時會相等,他們二者之間是沒有必然關係************************大數據

看3段重要代碼摘要:    this

public HashMap(int initialCapacity, float loadFactor) {
   int capacity = 1;
   while (capacity < initialCapacity)
      capacity <<= 1;

   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

有3個關鍵參數:
capacity:容量,就是數組大小
loadFactor:比例,用於擴容
threshold:=capacity*loadFactor   最多容納的Entry數,若是當前元素個數多於這個就要擴容(capacity擴大爲原來的2倍)指針

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

根據key算hash值,再根據hash值取得數組下標,經過數組下標取出鏈表,遍歷鏈表用equals取出對應key的value。ci

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

從數組(經過hash值)取得鏈表頭,而後經過equals比較key,若是相同,就覆蓋老的值,並返回老的值。(該key在hashmap中已存在)get

不然新增一個entry,返回null。新增的元素爲鏈表頭,之前相同數組位置的掛在後面。hash

另外:modCount是爲了不讀取一批數據時,在循環讀取的過程當中發生了修改,就拋異常it

  if (modCount != expectedModCount)
     throw new ConcurrentModificationException();


         

下面看添加一個map元素io

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


新增後,若是發現size大於threshold了,就resize到原來的2倍

    void resize(int newCapacity) {
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

新建一個數組,並將原來數據轉移過去

void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }


將原來數組中的鏈表一個個取出,而後遍歷鏈表中每一個元素,從新計算index並放入新數組。每一個處理的也放鏈表頭。

在取出原來數組鏈表後,將原來數組置空(爲了大數據量複製時更快的被垃圾回收?)

還有兩點注意:

static class Entry<K,V> implements Map.Entry<K,V>是hashmap的靜態內部類,iterator之類的是內部類,由於不是每一個元素都須要持有map的this指針。

HashMap把  transient Entry[] table;等變量置爲transient,而後override了readObject和writeObject,本身實現序列化。  

相關文章
相關標籤/搜索