1. 程式人生 > >簡單理解HashMap底層原理

簡單理解HashMap底層原理

HashMap底層是通過陣列加連結串列的結構來實現的。

HashMap 的例項有兩個引數影響其效能:“初始容量” 和 “載入因子”。

初始容量只是雜湊表在建立時的容量。載入因子 是雜湊表在其容量自動增加之前可以達到多滿的一種尺度。當雜湊表中的條目數超出了載入因子與當前容量的乘積時,通過呼叫 rehash 方法將容量翻倍。
說的通俗一點啊 比如說你要裝水 你首先找個一個桶 這個桶的容量就是載入容量,載入因子就是比如說你要控制在這個桶中的水要不超過水桶容量的多少,比如載入因子是0.75 那麼在裝水的時候這個桶最多能裝到3/4 處, 這麼已定義的話 你的桶就最多能裝水 = 桶的容量 * 載入因子
如果桶的容量是40 載入因子是0.75 那麼你的桶最多能裝40*0.75 = 30的水
如果你要裝的水比30 多 那麼就該用大一點的桶 而rehash就是負責增加桶的容量的方法

HashMap包括幾個重要的成員變數:table, size, threshold, loadFactor, modCount。

 table是一個Entry[]陣列型別,而Entry實際上就是一個單向連結串列。雜湊表的"key-value鍵值對"都是儲存在Entry陣列中的。 
 size是HashMap的大小,它是HashMap儲存的鍵值對的數量。 
 threshold是HashMap的閾值,用於判斷是否需要調整HashMap的容量。threshold的值="容量*載入因子",當HashMap中存               儲資料的數量達到threshold時,就需要將HashMap的容量加倍。
 loadFactor就是載入因子。 
 modCount是用來實現fail-fast機制的。

HashMap通過計算key的hash值。當hash值相同時,就會出現hash衝突,HashMap通過連結串列來解決衝突。

示例:

public class HashMapTest {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<>();
        map.put("key", "value");
        System.out.println(map.get("key"));
	}
}

1、put方法分析

//新增鍵值對方法
public V put(K key, V value) {
        //如果key為null,則hash值為0,將這個entry放在索引為0的地方,即table[0]
        if (key == null)
            return putForNullKey(value);
        //key不為null
        int hash = hash(key.hashCode());//計算key的hashCode的hash值
        int i = indexFor(hash, table.length);//返回hash值所在的索引位置
        //遍歷table[i]處的Entry,若存在key相同並且hash值相同,則用新元素替換舊元素
        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;
            }
        }
       //修改標記+1,然後進行元素新增操作
        modCount++;
        addEntry(hash, key, value, i);//將包含特定key、value和hash值的entry新增到特定的桶中
        return null;
    }

    //新增key為null的元素
    private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }
    /**
     * Adds a new entry with the specified key, value and hash code to
     * the specified bucket.  It is the responsibility of this
     * method to resize the table if appropriate.
     *
     * Subclass overrides this to alter the behavior of put method.
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }

2、get方法分析

/**
* 返回對映的key對應的value值,若map不包含該key,返回null
*/
public V get(Object key) {
        if (key == null)//獲取key為null的value值
            return getForNullKey();
        int hash = hash(key.hashCode());//計算key的hashCode的hash值
        //遍歷陣列中對應hash值的Entry連結串列,若Entry元素的hash值與hashCode的hash值相等並且key與查詢的key相等,則返回此Entry元素的value值
        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;
    }