即使我们在构造函数中指定的 initialCapacity 不是 2 的平方数,capacity 还是会被赋值为 2
的 N 次方。
为什么 Sun Microsystem 的工程师要将 hashMap key 空间的长度设为 2 的 N 次方呢?这里
参考 R.W.Floyed 给出的衡量散列思想的三个标准:
一个好的 hash 算法的计算应该是非常快的
一个好的 hash 算法应该是冲突极小化
如果存在冲突,应该是冲突均匀化
为了将各元素的 hashCode 保存至长度为 Length 的 key 数组中,一般采用取模的方式,即
index = hashCode % Length。不可避免的,存在多个不同对象的 hashCode 被安排在同
一位置,这就是我们平时所谓的“冲突”。如果仅仅是考虑元素均匀化与冲突极小化,似乎
应该将 Length 取为素数(尽管没有明显的理论来支持这一点,但数学家们通过大量的实践
得出结论,对素数取模的产生结果的无关性要大于其它数字)。为此,Craig Larman and
Rhett Guthrie《Java Performence》中对此也大加抨击。为了弄清楚这个问题,Bruce
Eckel(Thinking in JAVA 的作者)专程采访了 java.util.hashMap 的作者 Joshua Bloch,并
将他采用这种设计的原因放到了网上(http://www.roseindia.net/javatutorials/javahashmap
.shtml) 。
上述设计的原因在于,取模运算在包括 JAVA 在内的大多数语言中的效率都十分低下,而
当除数为 2 的 N 次方时,取模运算将退化为最简单的位运算,其效率明显提升(按照
Bruce Eckel 给出的数据,大约可以提升 5~8 倍) 。看看 JDK 中是如何实现的:
Java 代码
27 static int indexFor(int h, int length) {
28 return h & (length-1);
29 }
[java]view plain copy print ?
30 static int indexFor(int h, int length) {
31 return h & (length-1);
32 }
当 key 空间长度为 2 的 N 次方时,计算 hashCode 为 h 的元素的索引可以用简单的与操作
来代替笨拙的取模操作!假设某个对象的 hashCode 为 35(二进制为 100011),而