JDK1.8 HashMap ----resize源码解读

    科技2022-08-03  121

    /** * Initializes or doubles table size. If null, allocates in * accord with initial capacity target held in field threshold. * Otherwise, because we are using power-of-two expansion, the * elements from each bin must either stay at same index, or move * with a power of two offset in the new table. * 用来初始化或者扩容map的 * 如果为空则赋予初始的容量和阈值 * 如果要扩容,每个数组元素里的节点元素会移动到新表的相同位置或者 * 是原来位置加上扩容大小的偏移位置 * *返回值是一个新的节点类型的数组 * @return the table */ final Node<K,V>[] resize() { //创建node类型的数组存储旧的map数组 Node<K,V>[] oldTab = table; // 旧的数组容量(判断是否为空,为空就等于0不为空就等于旧的数组的长度) int oldCap = (oldTab == null) ? 0 : oldTab.length; //threshold表示当HashMap的size大于threshold时会执行resize操作。threshold=capacity*loadFactor //用于存储旧的hashmap的threshold值 int oldThr = threshold; //定义新的容量和新的threshold,初始值为0 int newCap, newThr = 0; //判断旧的容量值是否大于0 if (oldCap > 0) { //如果大于等于0在判断是否大于容量的最大值(容量的最大值是Integer的最大取值) 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); } //验证新的扩容阈值是否有值 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"}) //将新map数组的容量,初始化到新的节点数组中去 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; //将初始化好的数组赋值给全局的变量 table = newTab; //下面是扩容后数据的迁移的业务逻辑 //先判断旧的数组是否不为空 if (oldTab != null) { // 如果不为空就遍历每一个数组元素(桶位) for (int j = 0; j < oldCap; ++j) { //建立节点类型的变量e Node<K,V> e; //将第j个桶位的值赋值给e,然后在判断这个节点的值是否不为空 if ((e = oldTab[j]) != null) { //不为空的话,就将其置为空 oldTab[j] = null; //再判断e.next的值是否为空 。 if (e.next == null) //[e.hash & (newCap - 1)]算出此值放的桶位 //将e的值放在这个算出的桶位上 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; //以及一个指向下一节点的next指针 Node<K,V> next; //遍历桶位上的链 do { //将下一个节点赋给next next = e.next; //判断是将此节点放到扩容后的原位还是加偏移 // 注意((e.hash & oldCap)的结果要么是0 要么是oldCap) if ((e.hash & oldCap) == 0) { //如果等于0就放在扩容后数组的原位 //如果放原位的链里没有值 if (loTail == null) //将此几点赋值给原位链的头指针 loHead = e; else //有值的话就就让原位链的尾指针的next指向此节点 loTail.next = e; //在将尾指针指向next loTail = e; } //如果相与的结果不为0 //那就将此节点放入扩容数组的原位加偏移的位置(其他的操作是一样的) else { //判断偏移链 的尾指针是否为空(为空代表此链没有值) if (hiTail == null) //那就将此节点给偏移链的头指针 hiHead = e; else 有值的话就让偏移链的尾指针指向此节点 hiTail.next = e; 在将尾接点指向next 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; }
    Processed: 0.010, SQL: 8