返回

HashMap源码剖析:j + oldCap重分配公式揭秘

后端

在Java集合框架中,HashMap作为一种哈希表,以其高效的键值查找而著称。它的底层实现依赖于数组和链表,巧妙地利用了哈希函数将元素映射到特定的桶位置。然而,当哈希表容量不足或负载因子过高时,HashMap需要进行扩容操作,并将原有元素重新分布到新的桶位置。本文将深入剖析HashMap的源码,探究其扩容过程中的重分配公式:j + oldCap。

HashMap的扩容过程

当HashMap容量不足或负载因子过高时,触发扩容操作。扩容过程包括以下步骤:

  1. 创建新数组: 创建一个容量为旧容量两倍的新数组。
  2. 重新计算哈希值: 对于每个现有键值对,重新计算其哈希值,确定其在新数组中的桶位置。
  3. 重新分布元素: 将原有元素逐个重新分配到新数组对应的桶位置。

重分配公式:j + oldCap

在重新分布元素的過程中,HashMap使用了一个颇為巧妙的公式來計算新桶位置:j + oldCap,其中:

  • j:舊桶位置
  • oldCap:擴容前的容量

该公式可以避免哈希冲突,保证元素在扩容后的哈希表中均匀分布。其原理如下:

假设哈希函数对所有键值对取模后的结果都在[0, oldCap)范围内。

  • 原桶位置为j的元素: 扩容后,新的桶位置为j + oldCap,即旧桶位置加上旧容量。由于oldCap为2的幂,因此j + oldCap一定在[oldCap, 2 * oldCap)范围内。
  • 原桶位置为j + oldCap的元素: 扩容后,新的桶位置为j + oldCap + oldCap,即旧桶位置加上旧容量的2倍。由于oldCap为2的幂,因此j + oldCap + oldCap一定在[2 * oldCap, 3 * oldCap)范围内。

以此类推,对于所有原桶位置为j + k * oldCap的元素(其中k为非负整数),扩容后它们的新桶位置都会落在[k * oldCap, (k + 1) * oldCap)范围内。

因此,通过使用j + oldCap公式,HashMap可以保证在扩容后,所有元素在新哈希表中均匀分布,避免哈希冲突。

手写验证

为了验证上述公式的正确性,我们可以通过手写的方式进行计算:

假设原哈希表容量为8(oldCap = 8),哈希函数对所有键值对取模后的结果为:

[0, 1, 2, 3, 4, 5, 6, 7]

扩容后,哈希表容量变为16(newCap = 16)。根据公式,元素在新桶位置的计算如下:

原桶位置 新桶位置
0 0 + 8 = 8
1 1 + 8 = 9
2 2 + 8 = 10
3 3 + 8 = 11
4 4 + 8 = 12
5 5 + 8 = 13
6 6 + 8 = 14
7 7 + 8 = 15

可以看到,扩容后所有元素的新桶位置都在[8, 15]范围内,均匀分布。

总结

HashMap的重分配公式j + oldCap巧妙地利用了哈希函数的取模范围和容量倍增的特性,保证了扩容后元素在新哈希表中均匀分布。通过手写验证,我们进一步印证了公式的正确性。深入理解HashMap的扩容过程和重分配公式,有助于我们编写更高效的Java程序。