为什么HashMap的initialCapacity为11,而HashMap中的DEFAULT_INITIAL_CAPACITY为16且需要2的幂?

时间:2012-02-23 13:25:59

标签: java hashmap hashtable

比较JDK 1.6中的HashMapHashtable源代码,我在HashMap中看到了以下代码:

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

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

然而,在Hashtable中,我看到了这一点:

table = new Entry[initialCapacity];

public Hashtable() {
    this(11, 0.75f);
}

所以我的问题是: 为什么HashMap需要2的幂作为初始容量,而Hashtable选择11作为默认初始容量? 我认为这与Hashtable是线程安全的并且不允许空键或值的事情无关。

4 个答案:

答案 0 :(得分:22)

以下文章详细介绍了这个问题:HashMap requires a better hashCode() - JDK 1.4 Part II

根据那篇文章,转向两种尺寸的主要原因是位掩码比整数除法更快。这并非没有不良后果,原作者之一解释了这一点:

  

Joshua Bloch:使用二次幂的缺点是生成的哈希表对哈希函数(hashCode)的质量非常敏感。输入中的任何更改都必须影响哈希值的低位比特。 (理想情况下,它应该以相同的可能性影响散列值的所有位。)因为我们无法保证这是真的,所以当我们切换到2的幂时,我们放入次要(或“防御性”)散列函数哈希表。在屏蔽低阶位之前,将该散列函数应用于hashCode的结果。它的工作是将信息分散在所有位上,特别是分散到低位。当然它必须快速运行非常,否则你就失去了切换到两倍大小的表的好处。 1.4中的原始二级散列函数证明是不够的。我们知道这是理论上的可能性,但我们认为它不会影响任何实际数据集。我们错了。替换的二级哈希函数(我在计算机的帮助下开发)具有强大的统计特性,几乎可以保证良好的桶分布。

答案 1 :(得分:6)

Hashtable使用伪素数表大小并且增大表的大小相对较慢。 HashMap使用2的幂,比使用模数更快。

具有讽刺意味的是,2的幂的模数意味着需要一个好的hashCode(),因为顶部的位将被忽略,因此HashMap有一个重新排列hashCode的方法,以避免这个问题,这意味着它实际上可能更慢。位:Z

答案 2 :(得分:3)

这可能有所帮助:

http://www.concentric.net/~Ttwang/tech/primehash.htm

基本上,如果我没记错的话,当你的哈希表的大小为2的幂时,很容易根据密钥的相关位较小来获得哈希函数。

使用素数(如11)作为表的大小,使得表行的冲突不太可能,因此插入“更便宜”。

答案 3 :(得分:0)

表大小为2的幂的要求是一个实现细节,这个类的用户不知道 - 这就是为什么c'tor默默地将值调整为下一个更大的2的幂而不是标记错误。

Hashtable实现假设散列可能不是均匀分布的,因此它试图使用一些主要的bin,以避免散列的频率分布中的峰值。

这两个实现细节的组合会导致性能不佳。

(例如,原始哈希函数将是

int hash(String s, int nBins) {
    return s[0] % nBins;
}

如果nBins为32,则eE最终位于同一个bin中,因此散列值的分布与字母出现的分布相关,这些字母具有明显的峰值 - 所以频率分配将在32处达到峰值。)