HashMap核心源碼解讀

HashMap核心源碼解讀

本文爲CoderRuL原創,如需轉載請附上本文鏈接!

博主主頁:https://blog.csdn.net/qq_43472474

屬性

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

    /**
     * 序列號
     */
    private static final long serialVersionUID = 362498820763181265L;

    /**
     * HashMap數組結構的默認容量,容量必須爲2的整數次冪,此處爲16
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

    /**
     * HashMap數組的最大容量,其值必須爲小於等於2的30次冪且爲2的非負整數次冪
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * HashMap默認的裝載因子
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * HashMap鏈表樹化閾值,當鏈表長度大於等於該值時會轉化爲紅黑樹
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * HashMap紅黑樹鏈化的閾值,當紅黑樹的結點數小於等於該值時會轉化爲鏈表
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * HashMap鏈表樹化的最小數組容量,其值至少爲4*TREEIFY_THRESHOLD
     * 在將鏈表轉化爲紅黑樹之前,會先判斷數組的長度,如果數組長度小於64,
     * 那麼會選擇擴容數組,而不是轉化爲紅黑樹
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

    /**
     * HashMap的數組結構,其大小必須爲2的整數次冪
     */
    transient Node<K, V>[] table;

    /**
     * 存放所有的HashMap元素(Entry)
     */
    transient Set<Map.Entry<K, V>> entrySet;

    /**
     * HashMap的大小,即存放元素(Node)的數量
     */
    transient int size;

    /**
     * 每次擴容和更改Map結構的計數器
     */
    transient int modCount;

    /**
     * 臨界值(數組容量*裝載因子),當數組實際使用大小超過該值時會進行擴容
     */
    int threshold;

    /**
     * HashMap的裝載因子
     */
    final float loadFactor;
}

Node結點類

/**
 * HashMap鏈表結點,實現Entry接口
 */
static class Node<K, V> implements Map.Entry<K, V> {
    final int hash;  //hash值
    final K key; //鍵
    V value;  //值
    HashMap.Node<K, V> next;  //下一個結點指針

    Node(int hash, K key, V value, HashMap.Node<K, V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    public final K getKey() {
        return key;
    }

    public final V getValue() {
        return value;
    }

    public final String toString() {
        return key + "=" + value;
    }

    public final int hashCode() {
        //結點的hashCode由key的hashCode按位異或value的hashCode計算得出
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }

    /**
     * 設置結點的value,並返回舊的value
     * @param newValue 新值
     * @return 舊值
     */
    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

    /**
     * 兩個結點的鍵與值均相同時判定相同
     * @param o 待比較的對象
     * @return 鍵值是否相同
     */
    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
            if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

Hash靜態方法

/**
 * HashMap擾動函數,減少hash碰撞
 */
static final int hash(Object key) {
    int h;
    //^:按位異或運算,同爲0,異爲1
    //>>>:無符號右移,忽略符號位,空位以0補齊
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

/**
 * 如果對象x所屬的類實現了Comparable接口,返回x所屬的類,否則返回null
 */
static Class<?> comparableClassFor(Object x) {
    if (x instanceof Comparable) {
        Class<?> c;
        Type[] ts, as;
        Type t;
        ParameterizedType p;
        if ((c = x.getClass()) == String.class) // bypass checks
            return c;
        if ((ts = c.getGenericInterfaces()) != null) {
            for (int i = 0; i < ts.length; ++i) {
                if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType) t).getRawType() ==
                                Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                    return c;
            }
        }
    }
    return null;
}

/**
 * 如果x爲null或者x所屬的類不爲kc,返回0
 * 如果x所屬的類爲kc,返回k.compareTo(x)的結果
 */
@SuppressWarnings({"rawtypes", "unchecked"})
static int compareComparables(Class<?> kc, Object k, Object x) {
    return (x == null || x.getClass() != kc ? 0 :
            ((Comparable) k).compareTo(x));
}

/**
 * 返回第一個大於等於cap並且爲2的非負整數冪的數
 */
static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

HashMap構造方法

/**
 * @param initialCapacity 數組容量
 * @param loadFactor      裝載因子
 * @throws IllegalArgumentException 
 */
public HashMap(int initialCapacity, float loadFactor) {
    //檢查initialCapacity和loadFactor的合法性
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)	
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))	
        throw new IllegalArgumentException("Illegal load factor: " +
                loadFactor);
    this.loadFactor = loadFactor;
    //此處返回大於等於initialCapacity且爲2的非負整數冪的數
    this.threshold = tableSizeFor(initialCapacity);
}

/**
 * @param initialCapacity 數組容量
 * 裝載因子爲默認裝載因子
 * @throws IllegalArgumentException 
 */
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

/**
 * 默認構造方法
 * 數組容量爲默認容量16,裝載因子爲默認裝載因子
 */
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; 
}

/**
 * 使用另一個Map初始化的構造函數
 */
public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

put方法

/**
 * 調用putVal方法
 */
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

/**
 * @param hash         key的hash
 * @param key          key
 * @param value        value
 * @param onlyIfAbsent 如果爲true,不更新已存在的值
 * @param evict        初始化Map時爲false,已經初始化之後爲true
 * @return 舊值或不存在時爲null
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K, V>[] tab;   //數組
    Node<K, V> p;   //開始指向頭結點
    int n, i;   //n爲數組長度,i爲數組下標

    //數組爲空或者數組長度爲0,對數組進行擴容(初始化)
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;

    //i = (n-1) & hash一定小於等於n-1,所以i爲hash對應的數組下標
    //tab[i]沒有元素,直接插入結點到tab[i]
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K, V> e;   //記錄需插入的結點的位置
        K k;
        //key與頭結點key相同,前一個條件判斷key爲null的情況
        if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //頭結點爲紅黑樹結點
        else if (p instanceof TreeNode)
            e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
        //爲鏈表結點
        else {
            //從第二個結點開始遍歷鏈表
            for (int binCount = 0; ; ++binCount) {
                //在鏈表末尾插入結點,此時e爲null
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //結點數量達到閾值,轉化爲紅黑樹
                    if (binCount >= TREEIFY_THRESHOLD - 1)
                        treeifyBin(tab, hash);
                    break;
                }
                //鏈表的結點的key與插入的結點的key相等
                if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        //已經存在相同的key
        if (e != null) {
            V oldValue = e.value;//記錄key對應的舊值
            //onlyIfAbsent爲false或者舊值爲null,value更新爲新值
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            //返回舊值
            return oldValue;
        }
    }

    //不存在相同的key,新插入元素才執行下面的代碼
    ++modCount;
    //實際大小大於臨界值,擴容
    if (++size > threshold)
        resize();

    afterNodeInsertion(evict);
    return null;    //返回舊值爲null
}

/**
 * @param m 需put的map
 * @throws NullPointerException HashMap爲空時
 */
public void putAll(Map<? extends K, ? extends V> m) {
    putMapEntries(m, true);
}

get方法

/**
 * 調用getNode方法
 */
public V get(Object key) {
    Node<K, V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

/**
 * @param hash key的hash
 * @param key  key
 * @return 存在時返回對應的Node,否則返回null
 */
final Node<K, V> getNode(int hash, Object key) {
    Node<K, V>[] tab;
    Node<K, V> first, e;
    int n;
    K k;
    //數組不爲null且數組長度大於0,並且key對應下標的鏈表(或紅黑樹)不爲null
    if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
        //先判斷第一個結點是否是要get的結點
        if (first.hash == hash &&
                ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            //key對應下標處存放的是紅黑樹
            if (first instanceof TreeNode)
                return ((TreeNode<K, V>) first).getTreeNode(hash, key);
            //從鏈表第二個結點開始遍歷鏈表
            do {
                if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

擴容方法

/**
 * 初始化或擴容數組
 * @return the table
 */
final Node<K, V>[] resize() {
    //原數組
    Node<K, V>[] oldTab = table;
    //原數組的容量
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    //舊的臨界值
    int oldThr = threshold;
    //新的數組容量和臨界值
    int newCap, newThr = 0;

    //原數組容量大於0,即數組已經被初始化
    if (oldCap > 0) {
        //容量已經達到數組最大容量,不擴容
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        //原數組容量的2倍小於最大容量且原數組大於等於默認容量
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; //臨界值也擴大爲原臨界值的2倍
        //另一種省略的情況,原數組的2倍大於等於最大容量,這時是允許的
        //只有在當前容量大於等於最大容量時纔不允許擴容
    }
    //初始化容量放在原臨界值中
    else if (oldThr > 0)
        newCap = oldThr;  //新的容量爲原臨界值
    // 原臨界值爲0,即使用的是HashMap()構造方法
    else {
        newCap = DEFAULT_INITIAL_CAPACITY;  //容量設爲默認容量
        newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }

    //重新計算臨界值
    if (newThr == 0) {
        float ft = (float) newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?
                (int) ft : Integer.MAX_VALUE);
    }
    threshold = newThr;

    @SuppressWarnings({"rawtypes", "unchecked"})
    Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];
    table = newTab;
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            //把原數組的元素移動到新數組中
            Node<K, V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                //當前桶只有一個元素
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                //當前桶存放的是紅黑樹
                else if (e instanceof TreeNode)
                    ((TreeNode<K, V>) e).split(this, newTab, j, oldCap);
                else {
                    Node<K, V> loHead = null, loTail = null;
                    Node<K, V> hiHead = null, hiTail = null;
                    Node<K, V> next;
                    //將當前鏈表轉化爲兩條鏈表
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        } else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    //將兩條鏈表分別加到數組j和j+oldCap位置
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

remove方法

/**
 * 按照key移除HashMap中的元素
 * @param key key
 * 調用removeNode方法
 * @return 舊值或不存在時爲null
 */
public V remove(Object key) {
    Node<K, V> e;
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
}

/**
 * @param hash       key的hash
 * @param key        key
 * @param value      value
 * @param matchValue 爲true時只有當value相同才刪除
 * @param movable    爲false時在刪除時不刪除其他結點
 * @return key對應的Node或如果不存在爲null
 */
final Node<K, V> removeNode(int hash, Object key, Object value,
                            boolean matchValue, boolean movable) {
    Node<K, V>[] tab;
    Node<K, V> p;
    int n, index;
    //key索引處有元素
    if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
        Node<K, V> node = null, e;
        K k;
        V v;
        //判斷頭結點是否是要刪除的元素
        if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
        //索引處不只有一個結點
        else if ((e = p.next) != null) {
            //索引處桶存放的是紅黑樹
            if (p instanceof TreeNode)
                node = ((TreeNode<K, V>) p).getTreeNode(hash, key);
            //從第二個元素開始遍歷鏈表
            else {
                do {
                    if (e.hash == hash &&
                            ((k = e.key) == key ||
                                    (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        //存在鍵爲key的結點並且不要求value相同或值與value相同
        if (node != null && (!matchValue || (v = node.value) == value ||
                (value != null && value.equals(v)))) {
            //結點爲紅黑樹結點
            if (node instanceof TreeNode)
                ((TreeNode<K, V>) node).removeTreeNode(this, tab, movable);
            //結點爲頭結點
            else if (node == p)
                tab[index] = node.next;
            else
                p.next = node.next;
            ++modCount;
            --size;
            //檢查是否需要將紅黑樹轉化爲鏈表
            afterNodeRemoval(node);
            return node;
        }
    }
    return null;
}

/**
 * 刪除所有結點
 */
public void clear() {
    Node<K, V>[] tab;
    modCount++;
    if ((tab = table) != null && size > 0) {
        size = 0;
        for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

其他方法

/**
 * @param m     map
 * @param evict 初始化Map時爲false,已經初始化之後爲true
 */
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        //數組沒有初始化
        if (table == null) {
            //計算數組容量
            float ft = ((float) s / loadFactor) + 1.0F;
            int t = ((ft < (float) MAXIMUM_CAPACITY) ?
                    (int) ft : MAXIMUM_CAPACITY);
            if (t > threshold)
                threshold = tableSizeFor(t); 
        } 
        //數組還未初始化,m的容量大於臨界值,觸發擴容
        else if (s > threshold)
            resize();
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章