下面给出例子 , 比如从容量为16扩容到32时 , 画图表示:
进行扩容 , 扩大到原来的两倍:
到这一步 , 下标(n-1) & hash , 扩容后的数据10101和原来的00101相比 , 其实就是多了1bit , 10101是十进制的21 , 而21=5 16 , 就是“原位置 旧容量” , 还有另外一种情况是保持为0的情况 , 这种情况是不改变位置的
下面给出一份表格 , 数据如图:
容量为16的情况
有低位的两个指针loHead、lloTail , 高位的两个指针hiHead、hiTail
扩容到32之后 , 再两个链表加到对应位置 。分别有两种情况 , 保持原来位置的和“原位置 旧容量”这个位置
所以 , 扩容的过程 , 对应的节点位置改变是这样的过程:
7.3、resize的源码实现经过上面比较详细的分析 , 这个实现逻辑是可以在代码里找到对应的 , ok , 跟一下对应的源码:
final Node[] resize() {// 得到当前的节点数组Node[] oldTab = table;// 数组的长度int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;// 计算扩容后的大小if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) { // 超过最大容量 即 1 <<< 30// 超过最大容量就不扩充了 , 修改阀值为最大容量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// 使用默认值16newCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}// 重新计算阀值 , 然后要赋值给thresholdif (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}// 新的阀值 , 原来默认是12 , 现在变为24threshold = newThr;// 创建新的节点, newCap是新的数组长度 , 为32@SuppressWarnings({"rawtypes","unchecked"})Node[] newTab = (Node[])new Node[newCap];table = newTab;if (oldTab != null) {for (int j = 0; j < oldCap;j) {Node e;if ((e = oldTab[j]) != null) {oldTab[j] = null;if (e.next == null)newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)// 是红黑树节点 , 调用split方法((TreeNode)e).split(this, newTab, j, oldCap);else { // preserve order 是链表的情况// 定义相关的指针Node loHead = null, loTail = null;Node hiHead = null, hiTail = null;Node next;do {next = e.next;// 不需要移动位置if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else { // 需要移动位置 , 调整到“原位置 旧容量”这个位置if (hiTail == null)hiHead = e;else// hiTail指向要移动的节点ehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;// 位置不变newTab[j] = loHead;}if (hiTail != null) {// hiTail指向nullhiTail.next = null;// oldCap是旧容量 , 移动到“原位置 旧容量”这个位置newTab[joldCap] = hiHead;}}}}}return newTab;}8、Jdk8中HashMap的remove操作remove方法 , 这里思路是先要找到元素的位置 , 如果是链表 , 遍历链表remove元素就可以 , 红黑树的情况就遍历红黑树找到节点 , 然后remove树节点 , 如果这时候树节点数小于6 , 这种情况就要转链表
@Overridepublic boolean remove(Object key, Object value) {return removeNode(hash(key), key, value, true, true) != null;}final Node removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {Node[] tab; Node p; int n, index;// 数组下标是(n-1)&hash , 能找得到元素的情况if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) {Node
推荐阅读