为什么HashMap要求初始容量为2的幂?

Sus*_*ant 41 java hash hashtable hashmap

当我看到以下内容时,我正在浏览Java的HashMap源代码

//The default initial capacity - MUST be a power of two.
static final int DEFAULT_INITIAL_CAPACITY = 16;
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么这个要求首先存在?我还看到允许创建具有自定义容量的HashMap的构造函数将其转换为2的幂:

int capacity = 1;
while (capacity < initialCapacity)
  capacity <<= 1;
Run Code Online (Sandbox Code Playgroud)

为什么容量总是必须是2的幂?

此外,当执行自动重新散列时,究竟会发生什么?哈希函数也改变了吗?

Jon*_*eet 44

映射必须计算出用于任何给定键的内部表索引,将任何int值(可能为负)映射到范围中的值[0, table.length).何时table.length是2的幂,这可以非常便宜地完成- 并且在indexFor:

static int indexFor(int h, int length) {
    return h & (length-1);
}
Run Code Online (Sandbox Code Playgroud)

使用不同的表长度,您需要计算余数并确保它是非负的.这绝对是一个微优化,但可能是一个有效的:)

此外,当执行自动重新散列时,究竟会发生什么?哈希函数也改变了吗?

我不清楚你的意思.使用相同的哈希码(因为它们只是通过调用hashCode每个键来计算)但由于表长度的变化,它们将在表中以不同的方式分布.例如,当表长度为16时,5和21的哈希码最终都存储在表条目5中.当表长度增加到32时,它们将位于不同的条目中.

  • 在这里并不重要,但是Hashmap使用的键的哈希值不是`key.hashCode()`。哈希是在key.hashCode()顶部应用的补充哈希函数。这样做是为了防止较差的hashCode实现可能导致超出预期的冲突。 (3认同)

M P*_*oet 5

理想情况实际上是将素数大小用于的支持数组HashMap。这样,您的密钥将更自然地分布在整个阵列中。但是,这适用于mod除法,并且随着Java的每个发行版,该操作变得越来越慢。从某种意义上说,2幂的方法是您可以想象的最差的表大小,因为哈希码实现较差的实现更有可能在数组中产生键合。

因此,您将在Java的HashMap实现中找到另一个非常重要的方法,即hash(int),它可以补偿不良的哈希码。

  • 基本上,使用两种方法的功效使hashCode的低位成为重要的。对于较差的hashCode实现,此差别不会太大(例如:10110111和00000111)。因此,随着所有位的移位,较高的位变得更加重要。 (2认同)
  • 关于“ mod操作在Java的每个发行版中都变得越来越慢”的说法令人误解。而是,位掩码操作以更快的速度变得更快,最终这两者都开始反映出实际硬件的基本性能。在那个级别上,位掩码肯定具有更高的性能-足够使整个设置(包括附加的哈希码加扰步骤)仍然快得多。 (2认同)