Java HashMap 扩容过程分析

Posted cbhe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java HashMap 扩容过程分析相关的知识,希望对你有一定的参考价值。

 

  1 final Node<K, V> resize(){
  2 
  3     // 用于存储重新散列后的数组
  4     Node<K, V>[] newTab;
  5     
  6     // 如果原来的数组是空的,则resize执行的是初始化操作,而不是扩容操作
  7     if(table == null){
  8         // 初始容量为16
  9         int cap = DEFAULT_CAPACITY;// 1<<<4
 10 
 11         // 初始的加载因子为0.75,即容量使用到3/4时就要再次扩容
 12         loadFactor = DEFAULT_LOADFACTOR;
 13 
 14         // 设置扩容阈值
 15         threshold = (int)(cap * loadFactor);
 16 
 17         newTab = (Node<K, V>[])(new Node[cap]);
 18         return newTab;
 19     }
 20 
 21     // ... 省略了细节处理,例如容量到达最大值时应该怎么办,本程序段主要讲述扩容流程
 22 
 23     // 如果原来的数组不为空,则先扩容,再把原来的数据重新散列到新的扩容后的数组中
 24     int oldCap = table.length;
 25     int newCap = oldCap << 1;
 26     threshold = newCap * loadFactor;
 27     newTab = (Node<K, V>[])(new Node[newCap]);
 28 
 29     // 循环遍历原来的数组,进行重新散列
 30     for(int i = 0; i < table.length; i++){
 31 
 32         if(table[i] == null){
 33             continue;
 34         }
 35 
 36         Node<K,V> e = table[i];
 37         table[i] = null;
 38 
 39         // 数组中每个元素下都是一个链表
 40         // 如果链表只有一个元素,那么直接将其放到新位置
 41         if(e.next == null){
 42             newTab[e.hash & (newCap-1)] = e;
 43             continue;
 44         }
 45 
 46         // 如果链表中有多个元素,则需要将其分成两个链表
 47         // 为什么是两个链表呢?因为当 hash&(oldCap-1) == i 时,
 48         // hash&(newCap-1) 只会有两个取值:i 和 i+oldCap
 49         // 证明:
 50         //    假设 oldCap = 16 = 0b10000
 51         //    则   newCap = 16 <<< 1 = 0b100000
 52         //    则   oldCap - 1 = 0b1111
 53         //         newCap - 1 = 0b11111
 54         //    则 hash&(oldCap-1) 与 hash&(newCap-1) 只有 从又向左数第五位不同
 55         //    又因为 0b10000 = oldCap 
 56         //    所以   hash&(newCap-1) == hash&(oldCap-1) == i 
 57         //          或 hash&(newCap-1) == hash&(oldCap-1)+oldCap == i+oldCap
 58 
 59         // 链表1:放在原处,即 hash&(newCap-1) = hash&(oldCap-1) = i
 60         Node<K, V> hiHead, hiTail;
 61         // 链表2:放在 i+oldCap处,即hash&(newCap-1) = hash&(oldCap-1)+oldCap = i+oldCap
 62         Node<K, V> loHead, loTail;
 63         do{
 64             // 将原来链表中的元素分类放到链表1、2中
 65             
 66             // 如果散列值与原来一样(i)
 67             if((e.hash & (oldCap-1)) == (e.hash & (newCap-1))){
 68                 if(loHead == null){
 69                     loHead = e;
 70                 } else {
 71                     loTail.next = e;
 72                 }
 73                 loTail = e;
 74             } else {
 75                 // 如果散列值与原来不一样(i+oldCap)
 76                 if(hiHead == null){
 77                     hiHead = e;
 78                 } else {
 79                     hiTail.next = e;
 80                 }
 81                 hiTail = e;
 82             }
 83             e = e.next;
 84         } while(e != null);
 85 
 86         if(hiHead != null){
 87             hiTail.next = null;
 88             
 89             // 放在新位置
 90             newTab[i+oldCap] = hiHead;
 91         }
 92         if(loHead != null){
 93             loTail.next = null;
 94 
 95             // 放在原处
 96             newTab[i] = loHead;
 97         }
 98     }
 99 
100     return newTab;
101 }

 

以上是关于Java HashMap 扩容过程分析的主要内容,如果未能解决你的问题,请参考以下文章

Java8中HashMap扩容算法小计

HashMap 扩容分析

Java 集合深入理解 :HashMap之扩容 数据迁移

Java常见集合的默认大小及扩容机制

JDK1.8 HashMap 扩容 对链表(长度小于默认的8)处理时重新定位的过程

作为Java开发,知道HashMap底层存储原理总不会害你!