HashMap 源码

属性

默认长度

如不传入初始化长度,则默认长度为 16

/** * The default initial capacity - MUST be a power of two. */static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

阈值

所能包容的 key-value 对的极限,超过此值就须要进行扩容

/** * The next size value at which to resize (capacity * load factor).*/int threshold;

底层数组

HashMap 底层寄存 Node 节点的数组,在第一次应用的时候进行初始化,长度总为 2 的 N 次幂

HashMap 保障扩容后长度 n 总为 2 次方是因为计算 Node 所在索引时采纳了 (n - 1) & hash 运算进行优化(& 比 % 效率更高),等价于对 n 取模,也就是 h % n

/** * The table, initialized on first use, and resized as * necessary. When allocated, length is always a power of two. * (We also tolerate length zero in some operations to allow * bootstrapping mechanics that are currently not needed.) */transient Node<K,V>[] table;

负载因子

如不传入负载因子,默认为 0.75

负载因子过大会导致空间利用率较低,过小会导致碰撞概率变大,查问效率变低

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

扩容为树的阈值

当链表长度大于 8 时,会转变为红黑树

/** * The bin count threshold for using a tree rather than list for a * bin.  Bins are converted to trees when adding an element to a * bin with at least this many nodes. The value must be greater * than 2 and should be at least 8 to mesh with assumptions in * tree removal about conversion back to plain bins upon * shrinkage. */static final int TREEIFY_THRESHOLD = 8;

回退为链表的阈值

当树中节点少于 6 个时,会转变为链表

/** * The bin count threshold for untreeifying a (split) bin during a * resize operation. Should be less than TREEIFY_THRESHOLD, and at * most 6 to mesh with shrinkage detection under removal. */static final int UNTREEIFY_THRESHOLD = 6;

能够树化的最小底层数组长度

如果底层数组长度小于 64 时,阐明底层元素并不多,只是调配到某个地位的元素较多,先不转变为红黑树,先扩容底层数组扩散开

/** * The smallest table capacity for which bins may be treeified. * (Otherwise the table is resized if too many nodes in a bin.) * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts * between resizing and treeification thresholds. */static final int MIN_TREEIFY_CAPACITY = 64;

treeifyBin(Node<K,V>[] tab, int hash) 办法中,如果当先底层数组长度小于 MIN_TREEIFY_CAPACITY 会进行扩容

if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)    resize();

重要办法

hash 算法

采纳 hashCode() 的高 16 位异或低 16 位实现,能够保障高下 16 位都会参加到运算中

static final int hash(Object key) {    int h;    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

put 流程

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,               boolean evict) {    Node<K,V>[] tab; Node<K,V> p; int n, i;    // 第一次应用,底层数组还没有初始化,进行初始化    if ((tab = table) == null || (n = tab.length) == 0)        n = (tab = resize()).length;    // n 为底层数组长度,(n - 1) & hash 后果为[0,n-1]    // 如果为 null 则此处不存在任何 Node,间接新建 Node 放到此处即可    // HashMap 保障扩容后长度 n 总为 2 次方,因为 & 比 % 具备更高的效率    // 所以采纳了 (n - 1) & hash 运算进行优化,等价于对 n 取模,也就是 h % n    if ((p = tab[i = (n - 1) & hash]) == null)        tab[i] = newNode(hash, key, value, null);    else {        Node<K,V> e; K k;        // 判断 Key 是否曾经存在,如果 key 存在,间接解决抵触        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) {                // 遍历到了链表尾部,阐明链表中 key 不存在抵触                if ((e = p.next) == null) {                    // 把新的 Node 插入到链表尾部                    p.next = newNode(hash, key, value, null);                    // 在链表尾部新插入了一个 Node,此时链表长度为 binCount + 1                    // 相当于:链表长度 (binCount + 1) >= TREEIFY_THRESHOLD                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st                        // 转变为红黑树                        treeifyBin(tab, hash);                    break;                }                // 链表内有存在 Key,跳出循环,解决抵触                if (e.hash == hash &&                    ((k = e.key) == key || (key != null && key.equals(k))))                    break;                p = e;            }        }        // 解决 key 抵触的状况        if (e != null) { // existing mapping for key            V oldValue = e.value;            if (!onlyIfAbsent || oldValue == null)                // 把抵触的 Node 的 value 替换掉                e.value = value;            afterNodeAccess(e);            // 返回被替换的 Node 的 value            return oldValue;        }    }    ++modCount;    // 超过能包容 KV对的阈值,进行扩容    if (++size > threshold)        resize();    afterNodeInsertion(evict);    return null;}

扩容办法

final Node<K,V>[] resize() {    Node<K,V>[] oldTab = table;    int oldCap = (oldTab == null) ? 0 : oldTab.length;    int oldThr = threshold;    int newCap, newThr = 0;    if (oldCap > 0) {        // 超过最大值,不再进行扩容,此时会产生大量碰撞        if (oldCap >= MAXIMUM_CAPACITY) {            threshold = Integer.MAX_VALUE;            return oldTab;        }        // 扩容为原来的两倍        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&                    oldCap >= DEFAULT_INITIAL_CAPACITY)            newThr = oldThr << 1; // double threshold    }    else if (oldThr > 0) // initial capacity was placed in threshold        newCap = oldThr;    else {               // zero initial threshold signifies using defaults        newCap = DEFAULT_INITIAL_CAPACITY;        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);    }    // 计算新的 KV 对阈值    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 { // preserve order                    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);                    if (loTail != null) {                        loTail.next = null;                        newTab[j] = loHead;                    }                    if (hiTail != null) {                        hiTail.next = null;                        newTab[j + oldCap] = hiHead;                    }                }            }        }    }    return newTab;}