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

问题描述:

我正想通过Java的HashMap的源代码,当我看到下面为什么HashMap要求初始容量是2的幂次?

//The default initial capacity - MUST be a power of two. 
static final int DEFAULT_INITIAL_CAPACITY = 16; 

我的问题是,为什么摆在首位不存在这个要求?我也看到,它允许使用自定义能力创建一个HashMap构造函数将其转换成二的幂:

int capacity = 1; 
while (capacity < initialCapacity) 
    capacity <<= 1; 

为什么总是有能力为二的幂?

此外,当执行自动重新刷新时,到底发生了什么?哈希函数是否也改变了?

该映射必须计算出哪个内部表索引可用于任何给定键,将任何int值(可能为负)映射到[0, table.length)范围内的值。当table.length是两个电源,可以做真的便宜 - 并且是在indexFor

static int indexFor(int h, int length) { 
    return h & (length-1); 
} 

具有不同表的长度,你需要计算余数,并确保它的非负面的。这绝对是一个微型优化,但可能是一个有效的:)

此外,什么时候执行自动刷新,究竟发生了什么?哈希函数是否也改变了?

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

+0

正是我在找什么,谢谢。 还有一个疑问,为什么Entry表暂时存在,即使它保留了所有数据? – Sushant

+1

@Sushant:表中的数据*在writeObject中显式*序列化(这样所有的空条目都不会被写出)。使字段瞬态停止来自*也*的正常序列化代码,将其写入'defaultWriteObject'的调用中。 –

+0

@JonSkeet h&(长度-1)如何处理底片?可以说长度= 16和h = -7 – Geek

理想情况实际上是使用素数大小作为HashMap的支持阵列。这样你的密钥将更自然地分布在阵列中。然而,这与mod分区协同工作,并且随着每个Java版本的发布,操作变得越来越慢。 从某种意义上说,2种方法的威力是您可以想象的最差的表格大小,因为糟糕的哈希码实现更有可能在阵列中产生关键的爆炸。

为此,你会发现在Java的HashMap实现,这是hash(int),用于补偿差哈希码另一种非常重要的方法。

+0

是的,这有很大的意义,但作为一个额外的好处,你可以详细讨论hash(int)函数如何改善原始哈希码。我看到它采取了几个比特,但我还没有完全理解它。 – Sushant

+1

基本上,使用两种方法的幂使得hashCode的低位是重要的。在糟糕的hashCode实现中,这个差别不会太大(例如:10110111和00000111)。所以随着所有位的转移,更高的位变得更加重要。 –

+0

嗯,我明白了......感谢 – Sushant