当我看到以下
时,我正在浏览Java的HashMap源代码//The default initial capacity - MUST be a power of two.
static final int DEFAULT_INITIAL_CAPACITY = 16;
我的问题是为什么这个要求首先存在?我还看到允许创建具有自定义容量的HashMap的构造函数将其转换为2的幂:
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
为什么容量总是必须是2的幂?
此外,当执行自动重组时,究竟会发生什么?哈希函数也改变了吗?
答案 0 :(得分:43)
映射必须确定要用于任何给定键的内部表索引,将任何int
值(可能为负)映射到范围[0, table.length)
中的值。当table.length
是2的幂时,可以真正廉价地完成 - 并且在indexFor
中:
static int indexFor(int h, int length) {
return h & (length-1);
}
使用不同的表长度,您需要计算余数并确保它是非负的。这绝对是微优化,但可能是有效的:)
此外,当执行自动重组时,究竟会发生什么?哈希函数也改变了吗?
我不太清楚你的意思。使用相同的哈希码(因为它们只是通过在每个键上调用hashCode
来计算),但由于表长度的变化,它们将在表中以不同的方式分布。例如,当表长度为16时,5和21的哈希码最终都存储在表条目5中。当表长度增加到32时,它们将位于不同的条目中。
答案 1 :(得分:4)
理想情况实际上是使用HashMap
的后备数组的素数大小。这样,您的密钥将更自然地分布在整个阵列中。然而,这适用于mod分区,并且每次发布Java时操作都变得越来越慢。
从某种意义上说,2方法的强大功能是您可以想象的最差的表大小,因为糟糕的哈希码实现更有可能在阵列中产生密钥崩溃。
因此,您将在Java的HashMap
实现中找到另一个非常重要的方法,即hash(int)
,它可以补偿糟糕的哈希码。