为什么使用2的幂实现HashMap?

时间:2018-11-28 19:31:14

标签: java hashmap

我的问题是,为什么哈希映射存储桶大小是2的幂,我在stackoverflow上经历了很多答案,但我仍然不相信。原因是:

  1. 我已经阅读到容量为2的幂使得计算索引的操作更加有效,因此我想知道在这里它到底有多有用。我的大小可能是3的幂,我仍然可以执行(hash)&(length-1)这样的&操作,所以为什么它确切应该是2的幂?

  2. 如果容量不是2的幂,为什么我需要进行余数运算?

3 个答案:

答案 0 :(得分:4)

无论如何,您都需要执行余数运算以获取哈希码(可以是任何int)以映射到哈希表中的条目。

m是2的幂且仅 的情况下,a % m等于a & (m - 1)。没有其他情况可以使用&计算余数。

答案 1 :(得分:4)

当您从2的幂中减去1时,得到的是一个二进制表示形式均为1的数字。 16是2的幂。如果从中减去1,则得到15,其二进制表示形式是1111。现在,如果对1111进行任意数字的按位与运算,您将得到整数的最后4位。换句话说,它等于16的模数(除法运算通常是昂贵的运算。因此,按位运算通常比除法运算更可取)。最后4位将计算为从0到15的任何数字,这是基础数组的索引。

您可以改为17号。在这种情况下,从中减去1后,您将获得16,即10000的二进制值。现在,您要对数字与16进行位与运算,您将丢失数字的所有位(末尾第5位除外)。因此,无论您使用多少数字,数组索引都将为16或0。这意味着您将发生很多冲突,这又意味着性能很差。代替O(1)进行检索,您需要O(log n),因为发生冲突时,给定存储桶中的所有节点都将存储在红黑树中。不仅。如果您在多线程环境中使用ConcurrentHashMap,则会遇到很多同步,因为所有新添加的内容最终只会存储在很少的存储桶中(在上述情况下,只有两个-0和16 ),然后在已经具有其他节点的存储桶中添加新节点时,该存储桶将被锁定,以避免由于多个线程进行修改而导致数据不一致。因此,其他尝试添加新节点的线程需要等待,直到当前线程释放锁为止。

最后,我还应该提到Java HashMap实现也将键的哈希码向右移16位,并在执行与(length-1 ),以确保还捕获了高阶位的影响。

因此,基本上,要点是,如果大小是2的幂,则密钥将在阵列中更均匀地分布,而冲突最小,从而导致更好的检索性能(在ConcurrentHashMap情况下,同步也较少) )与其他非2的幂的大小进行比较。

答案 2 :(得分:2)

我可以想到两个原因:

  1. 两者的幂使时间复杂性分析更加容易,因为在谈论计算时,log通常被假定为基数2。(请注意,实际上,可以证明所有log时间复杂度是等效的,无论其底数如何,但是如果您使用2的幂,则会使推理变得更容易,因为您的条件都是乘除以2)

  2. 两个的幂很好地匹配了硬件。将内存中的数字加倍要比将其乘以3少。同样,内存的大小都是2的幂,因此,如果您始终加倍,则总是会占用2 ^ n个 full 字节。