1. 程式人生 > >hashMap原始碼閱讀筆記1.7

hashMap原始碼閱讀筆記1.7

這幾天一直在看hashMap的原始碼,也借鑑了很多大佬的文章以便更好的理解,也從大佬文章中借鑑了很多的內容,如果侵權,請告知,我將立刻刪除

hashMap繼承AbstractMap,實現了map介面。

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

初始容量:雜湊表建立時的容量,實際上就是Entry<k,v>[] table的容量
static final int DEFAULT_INITIAL_CAPACITY = 16;
最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
載入因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
雜湊表
transient Entry<K,V>[] table;
存在對映關係的條數
transient int size;
閾值
int threshold;
修改計數
transient int modCount;

hashMap的幾個預設構造方法:
(1):構造了一個對映關係與指定map相同的hashMap,類似拷貝
public HashMap(Map<? extends K, ? extends V> m)
(2):指定初始容量和載入因子
public HashMap(int initialCapacity, float loadFactor)
(3):指定初始容量
public HashMap(int initialCapacity)
(4):預設構造方法
public HashMap()

計算hash值

final int hash(Object k) {
    int h = 0;
    if (useAltHashing) {
        if (k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
        h = hashSeed;
    }
    h ^= k.hashCode();
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

返回索引下標

//1:h& (length-1)運算等價於對length取模,也就是h%length,但是&比%具有更高的效
//2:capacity 必須為2的n次冪,則length-1肯定為奇數,在位運算h & (length -1)唯一性更高,
//減少collision的發生,也就是保證bucketIndex低重複性
static int indexFor(int h, int length) {        
    return h & (length-1);
}

當前map的對映關係數目

public int size() {
    return size;
}

判斷map是否為空,即對映條數是否為0

public boolean isEmpty() {
    return size == 0;
}

0.單項鍊表的實現 

    // 實現了單向連結串列
    static class Entry<K, V> implements Map.Entry<K, V> {
        final K key;
        V value;
        Entry<K, V> next;
        int hash;
        //Map.Entry儲存一個鍵值對 和這個鍵值對持有指向下一個鍵值對的引用,如此就構成了連結串列了。
        Entry(int h, K k, V v, Entry<K, V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
    }

1.get方法

當呼叫get方法,會首先判斷key值是否為null,如果為null,呼叫getForNullKey()方法,這也是hashMap允許key值為null的原因 

public V get(Object key) {
    if (key == null){
        return getForNullKey();
    }
    Entry<K,V> entry = getEntry(key);
    return null == entry ? null : entry.getValue();
}

當get(null)呼叫該方法,如果存在key為null情況,就返回key為null的value值,否則,直接返回null

private V getForNullKey() {
    for (Entry<K, V> e = table[0]; e != null; e = e.next) {
        if (e.key == null){
            return e.value;
        }
    }
    return null;
}

 當get(!=null)呼叫此方法

final Entry<K,V> getEntry(Object key) {
    1.根據key計算hash值
    int hash = (key == null) ? 0 : hash(key);
    2.通過hash值找出entry
    // 1 indexFor(hash, table.length):獲取bucketIndex
    // 2 遍歷具體bucket[indexFor(hash, table.length)]下的單向連結串列
    // 3 如果這個key的hashCode一樣同時key值一樣,或者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;
    }
    3.雜湊表中不存在key的時候返回null:例如:containsKey方法
    return null;
}

2.put方法

    public V put(K key, V value) {
        if (key == null)
            //當key為null時,呼叫putForNullKey方法
            return putForNullKey(value);
        //計算hash值
        int hash = hash(key);
        //獲取
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果執行put操作的時候發現key已存在,就更新value
            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不一致,就會導致ConcurrentModificationException
        //2:遍歷的時候執行remove操作也會導致ConcurrentModificationException
        //可以使用iterator解決2問題,1問題也說明hashMap是執行緒不安全
        //使hashMap執行緒安全:Map map = Collections.synchronizeMap(hashMap);
        modCount++;
        //不存在就新增
        addEntry(hash, key, value, i);
        return null;
    }

        
    void addEntry(int hash, K key, V value, int bucketIndex) {
        //如果對映關係的數量已經超過了閾值,並且table的桶索引的值不為null
        if ((size >= threshold) && (null != table[bucketIndex])) {
           //擴大一倍
            resize(2 * table.length);
            //重新計算hash值:結構已經改變
            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) {
        //斷開連結串列,add結點,注意總是從連結串列的表頭處插入新結點
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

引數連結:https://blog.csdn.net/lemon89/article/details/50994691