7000 字说清楚 HashMap,面试点都在里面了 (3)

另外,随之键值对添加的越来越多,HashMap的 size 越来越大,注意 size 前面说了,是实际的键值对数量,那么 size 到了多少就要扩容了呢,并不是等 size 和 threshold(容量)一样大了才扩容,而是到了阈值就开始扩容,阈值上面也说了,是容量 x 负载因子。

为什么放在一起说呢,因为首次初始化和扩容都是用的同一个方法,叫做 resize()。以下是我注释的 resize()方法。

final HashMap.Node<K,V>[] resize() { // 保存 table 副本,接下来 copy 到新数组用 HashMap.Node<K,V>[] oldTab = table; // 当前 table 的容量,是 length 而不是 size int oldCap = (oldTab == null) ? 0 : oldTab.length; // 当前桶大小 int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { //如果当前容量大于 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) // 初始化的场景(给定默认容量),比如 new HashMap(32) newCap = oldThr; //将容量设置为 threshold 的值 else { // 无参数初始化场景,new HashMap() // 容量设置为 DEFAULT_INITIAL_CAPACITY 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"}) HashMap.Node<K,V>[] newTab = (HashMap.Node<K,V>[])new HashMap.Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { HashMap.Node<K,V> e; //遍历 获得得到元素 赋给 e if ((e = oldTab[j]) != null) { //如果当前桶不为空 oldTab[j] = null; // 置空回收 if (e.next == null) //节点 next为空的话 重新寻找落点 newTab[e.hash & (newCap - 1)] = e; else if (e instanceof HashMap.TreeNode) //如果是树节点 //红黑树节点单独处理 ((HashMap.TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // 保持原顺序 HashMap.Node<K,V> loHead = null, loTail = null; HashMap.Node<K,V> hiHead = null, hiTail = null; HashMap.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; }

首次初始化

put方法中线先检查 table 数组是否为空,如果为空就初始化。

if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypgjp.html