引入
我们知道,hash方法的功能是根据Key来定位这个K-V在链表数组中的位置的。也就是hash方法的输入应该是个Object类型的Key,输出应该是个int类型的数组下标。如果让你设计这个方法,你会怎么做?
其实简单,我们只要调用Object对象的hashCode()方法,该方法会返回一个整数,然后用这个数对HashMap的容量进行取模就行了。
如果真的是这么简单的话,那HashMap的容量设置就会简单很多了,但是考虑到效率等问题,HashMap的hash方法实现还是有一定的复杂的。
hash的实现
主要涉及到两个部分:
1.计算hash
//该方法主要是将Object转换成一个整型。
//>>>无符号右移
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这里为什么要右移16位呢?
因为这个方法计算出来的hash值,到后面需要和数组的长度tab.length进行&运算,但由于绝大多数情况下length一般都小于2^16即小于65536。所以h & (length-1);结果始终是h的低16位与(length-1)进行&运算。要是高16位也参与运算,会让得到的下标更加散列。所以这里做了一个
h = key.hashCode()) ^ (h >>> 16);
让他重新生成新的hash,减少hash冲突
2.将hash生成的整型转换成链表数组中的下标
n = tab.length
p = tab[i = (n - 1) & hash]
这里其实就是求模的操作,当length是2的n次方的时候hash%length==hash&(length-1)等价,这里也解释了为什么hashmap的容量一定是2的n次方