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

在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的幂作为初始容量? 而哈希表选择11作为默认初始容量? 我认为这与哈希表是线程安全的并且不允许空键或值的事情无关。

谢谢。

以下文章详细讨论了这个问题: HashMap需要更好的hashCode() – JDK 1.4 Part II 。

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

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

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

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

这有助于:

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

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

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

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

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

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

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

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

如果nBins为32,则eE最终位于相同的bin中,因此散列值的分布与字母出现的分布相关,其具有明显的峰值 – 因此频率分布将在32处具有峰值。)