源碼分析HashMap的幾個問題(JDK1.7)

  • 如何存儲數據 (put、get)算法

  1. put數據數組

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        //計算key的hash值
        int hash = hash(key);
        //根據hash和數組的長度計算索引,即數組存放的位置
        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++;
        //將算出的key的hash值,key,value ,以及索引存放在entry對象中,並加入table數組中
        addEntry(hash, key, value, i);
        return null;
    }
  1. get數據多線程

public V get(Object key) {
    //若是key爲null,則返回null對於的value
    if (key == null)
        return getForNullKey();
    //根據key獲取entry
    Entry<K,V> entry = getEntry(key);
    return null == entry ? null : entry.getValue();
}
final Entry<K,V> getEntry(Object key) {
    if (size == 0) {
        return null;
    }
    //計算出key的hash值
    int hash = (key == null) ? 0 : hash(key);
    //根據hash值獲取索引,遍歷索引下的全部的entry值
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        //若是hash值相同而且key也相同則返回value
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            return e;
    }
    return null;
}
  • 默認容量爲何是16this

//1前移4位 二進制1是 01 ,前移4位 則是 10000,即16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

咱們先看一下源碼如何獲取index的值線程

   static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
        //hash 值與 數組的長度-1進行位運算,長度必須是2的冪。
        return h & (length-1);
    }

index的值是經過key的hash值與數組長度-1進行位運算來的。舉個例子,code

  1. 計算book的hashcode,結果爲十進制的3029737,二進制的101110001110101110 1001;對象

  2. HashMap長度是默認的16,計算Length-1的結果爲十進制的15,二進制的1111blog

  3. 把以上兩個結果作與運算,101110001110101110 1001 & 1111 = 1001,十進制是9,因此 index=9
    能夠說,Hash算法最終獲得的index結果,徹底取決於Key的Hashcode值的最後幾位。索引

==長度16或者其餘2的冪,Length-1的值是全部二進制位全爲1,這種狀況下,index的結果等同於HashCode後幾位的值。只要輸入的HashCode自己分佈均勻,Hash算法的結果就是均勻的。==ci

  • 負載因子爲何是0.75f

/**
 * The load factor used when none specified in constructor.
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;

加載因子須要在時間和空間成本上尋求一種折衷。

  1. 加載因子太高,例如爲1,雖然減小了空間開銷,提升了空間利用率,但同時也增長了查詢時間成本

  2. 加載因子太低,例如0.5,雖然能夠減小查詢時間成本,可是空間利用率很低,同時提升了rehash操做的次數。

  • hash衝突的產生與解決

  1. 產生

例如咱們如今要加入一個key=A,value=a,先須要計算A的index,假如是2。那麼結果以下:

圖1


再加入一個key=A', value=a',此時恰好計算A'的index也是2,這時候就會產生衝突。

 

  1. 解決hash衝突

這個時候咱們須要使用鏈表來解決hash衝突,當產生相同的index是,則經過Next指向同一個index的下一個節點,如圖:
(

圖二

)
注意:新來的Entry節點插入鏈表時,使用的是「頭插法」。至於爲何不插入鏈表尾部,由於HashMap的做者發現後插入的Entry被查找的可能性更大

 

  • 何時會出現死鎖

當擴容時,須要將老的table的數據轉移到新的table上,調用transfer發放進行轉移。
咱們先看下transfer方法

void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    //循環遍歷老的table
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next;
            //判斷是否須要從新計算hash值
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

正常狀況下,轉移前鏈表順序是1->2->3,逆序轉移後新的t順序變成 3->2->1,可是在多線程環境中,使用HashMap進行put操做時會引發死循環,致使死循環的緣由是在多線程的狀況下,會造成環形鏈表,這時是沒有問題的,咱們再看看get()方法中的獲取Entry的方法

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方法找一個不存在的值的時候,for循環將會死循環,即會造成死鎖

相關文章
相關標籤/搜索