为什么HashMap不用取余操作来计算下标?
wys521 2024-11-17 17:01:01 精选教程 19 ℃ 0 评论
性能考虑
- 在早期的 Java 版本中,HashMap确实是使用取余操作(%)来计算下标的。但是取余操作在计算机底层的实现相对复杂,特别是对于处理器来说,除法和取余操作通常比其他算术运算(如位运算)要慢。
- 取余操作的缺点:取余操作在硬件层面涉及到除法运算。处理器执行除法指令时,通常需要更多的时钟周期来完成计算。例如,在一些简单的处理器架构中,除法可能需要通过一系列复杂的移位、减法等操作来模拟实现,这使得其执行速度明显慢于加法、减法和位运算等基本运算。取余操作的性能还可能受到操作数大小的影响。对于较大的整数进行取余,其计算成本会更高。而且在不同的硬件平台和编译器优化下,取余操作的性能表现也不稳定。
- 位运算的优点:现代HashMap的实现使用位运算(&)来替代取余操作计算下标,因为位运算在处理器中执行速度更快。位运算在硬件层面可以直接通过电路的逻辑门(如与门、或门、非门等)来实现,速度非常快。例如,对于按位与操作(&),处理器只需要比较两个二进制数的对应位,然后根据逻辑规则生成结果,几乎不需要额外的复杂计算步骤。位运算的执行时间通常是固定的,不受操作数大小的影响。无论操作数是 8 位、16 位还是 32 位整数,按位与操作的时间复杂度都是常数级别。这使得位运算在处理大规模数据或者频繁计算下标的场景中,能够提供更稳定和高效的性能。例如,在HashMap频繁地根据键的hashCode计算存储位置时,位运算的稳定性优势就很明显。
数组长度的限制与优化
- 为了能够使用位运算来替代取余操作,HashMap对数组长度(capacity)有一定的要求。它的容量(数组长度)总是 2 的幂次方。这样做的好处是,当数组长度为 2 的幂次方时,hashCode & (length - 1)操作等价于hashCode % length。
- 例如,假设数组长度length = 16(2 的 4 次方),对于一个hashCode值为20的键,20 & (16 - 1)(即20 & 15)的结果和20 % 16的结果是相同的,都是 4。通过这种方式,HashMap在保证计算下标的功能等价的同时,利用了位运算的高效性。
哈希冲突的处理关联性
- HashMap使用位运算计算下标也与它处理哈希冲突的方式有关。在发生哈希冲突时,HashMap使用链表(在 Java 8 以后,当链表长度达到一定阈值会转换为红黑树)来存储具有相同下标的键值对。
- 位运算的优点(在哈希冲突方面):采用位运算计算下标可以更好地将键均匀地分布在数组中,减少哈希冲突的概率。因为 2 的幂次方长度的数组在与hashCode进行位运算时,能够利用hashCode的二进制位信息,使得不同的hashCode值有更大概率分布到不同的下标位置。相比之下,取余操作在处理哈希冲突方面没有这种与HashMap内部数据结构紧密结合的优势。位运算的结果具有一定的规律,有助于HashMap在处理哈希冲突时进行优化。例如,在扩容或者重新哈希(rehash)操作时,由于位运算和数组长度的 2 的幂次方特性,更容易对键值对进行重新分布,降低数据迁移的复杂性,提高HashMap在动态变化环境下的性能和稳定性。
本文暂时没有评论,来添加一个吧(●'◡'●)