HashMap源码剖析:j + oldCap重分配公式揭秘
2024-02-16 23:01:31
在Java集合框架中,HashMap作为一种哈希表,以其高效的键值查找而著称。它的底层实现依赖于数组和链表,巧妙地利用了哈希函数将元素映射到特定的桶位置。然而,当哈希表容量不足或负载因子过高时,HashMap需要进行扩容操作,并将原有元素重新分布到新的桶位置。本文将深入剖析HashMap的源码,探究其扩容过程中的重分配公式:j + oldCap。
HashMap的扩容过程
当HashMap容量不足或负载因子过高时,触发扩容操作。扩容过程包括以下步骤:
- 创建新数组: 创建一个容量为旧容量两倍的新数组。
- 重新计算哈希值: 对于每个现有键值对,重新计算其哈希值,确定其在新数组中的桶位置。
- 重新分布元素: 将原有元素逐个重新分配到新数组对应的桶位置。
重分配公式: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程序。