今天看到了HashMap,发现其中有一个方法很奇怪,叫indexFor(int h, int length),这个方法返回的是某个hashcode对应到hash table的下标位置,代码是这么实现的:
static int indexFor(int h, int length) {
return h & (length-1);
}
看了半天才搞懂为什么这么写,其实HashMap中放数据的还是数组,当往map里put值时就是向数组中添加数据,只不过数据的位置时根据put的key来计算的。之前学hashtable的时候计算位置都是用hashcode%length的。但是HashMap中却不是这么做的。
我们假设现在hashmap的length为16,第一个hashcode的值为7.,那么根据上面的代码,计算过程应该是:
7&(16-1)
转换成二进制来计算就是:
0111
&
1111
计算的结果为111,也就是7,我们会发现这个值与7%16的结果是一致的。让我们再测试一个hashcode为33。
二进制的计算过程是:
100001
&
001111
计算结果为000001,这与33%16结果又是相同的。
经过几次模拟计算可以发现,length-1的二进制值中每一位都是1,并且HashMap中保证了map的长度永远是2的N次方,而2的N次方-1的值的二进制每一位都是1.。所以当我们拿hashcode的值来计算的时候,如果hashcode二进制位数是小于等于length-1的二进制位数,那么与计算结果就是hashcode的值,如果前者位数大于后者位数,则与计算时会将多出来的位与0相与,则结果必然时0,这就导致超出位的值被省去了,身下的值也就是hashcode%llength的值了。至于为什么不直接用hashcode%length,是因为位运算要比模运算快一些。
解释到这里应该已经清楚这行代码的意思了,在这里不得不感叹,JDK的作者水平还是高,想法已经不是在如何实现上了,而是怎么实现效率更高。