在 Java 中,HashMap 是一个非常重要的集合类,它采用哈希表来存储键值对。当数据量变大时,需要调整哈希表的大小,以保证更好的性能和空间使用。resize() 方法就是 HashMap 进行扩容或缩减容量的方法。
什么情况下需要 resize() 方法
当 HashMap 存储的数据超过了负载因子乘以当前容量时(即元素个数 > 负载因子 * 容量),就需要进行 resize 操作。负载因子通常为 0.75,在之前的 JDK 版本中是默认值为 0.75,JDK 1.8 开始可以自定义负载因子。
resize 操作的目的是为了让 HashMap 在扩容或缩减容量后仍然能保持较低的查找和插入成本。如果 HashMap 中的键值对数量过多,则会导致哈希冲突概率增加,从而影响 HashMap 的性能。如果数量太少,则会造成空间浪费。
实现原理
HashMap 的 resize() 方法的实现原理涉及到两个主要步骤:
- 创建新的数组并重新分配元素:首先根据新的容量创建一个新的数组,然后遍历原来的数组,并将所有元素重新分配到新数组中。
- 调整哈希值:重新分配元素时,需要调整每个键值对的哈希值。这是因为在 Java 中,哈希值是由对象的 hashCode() 方法计算出来的。而当数组大小改变时,hashCode() 值需要重新计算。
具体实现过程可以通过以下源码逐行解释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| 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; } else if (oldThr > 0) newCap = oldThr; else { 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"}) 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 { 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; }
|
resize() 方法的时间复杂度
resize 操作需要遍历整个数组,并将元素重新分配到新数组中。因此,时间复杂度为 O(n),其中 n 是 HashMap 中键值对的数量。
总结
HashMap 的 resize() 方法是用来扩容或者缩减 HashMap 容量的方法。它通过重新分配元素和调整哈希值来实现。当 HashMap 中的键值对数量超过负载因子乘以当前容量时,就需要进行 resize 操作。为了保证 HashMap 的性能和空间利用率,我们可以通过调整负载因子和初始容量来优化 HashMap 的 resize 操作。