引入
我们知道hash算法的目的在于,让我们存储的值更更更加散散散列的存储数据;
那要分析这个hash算法那就离不开寻址算法了,hashmap怎么决定存储位置的?
寻址算法
这是hashmap的put方法,我们可以发现hashmap的寻址算法:
(length - 1) & hash //决定值的存储位置index
寻址算法为什么这样设计?
hashmap的length是2的n次幂
length可能的值:2/4/8/16…
因为有&运算,我们转换成二进制:
( 2-1) :0000 … 0001
( 4-1) :0000 … 0011
( 8-1) :0000 … 0111
(16-1):0000 … 1111
…
&运算的结果是都为1,结果才是1;
例子:hash & (16-1)
1010 1010 1001 1111 0000 0110 1100 0011
0000 0000 0000 0000 0000 0000 0000 1111
结果: 0000 … 0011
hash &(length -1)的结果就是只会保留(length - 1)的低位数据,而且是不会大于(length -1);
这样就有效的控制了寻址算法的值刚好在我们定义的length范围内。
这也是为什么hashmap保证length是2的n次幂的原因
hash算法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
直接看hash()的源码
拿到hashcode了为什么还要右移16位?还做了一次^运算?
key==null
hashmap是可以存key=null;
从代码看key==null的时候默认决定了位置index=0
hashmap是数组+链表;值的注意的是,null只是存储在数组index为0的位置,而具体在链表的哪个位置是不确定的。
key!=null
hashcode:一个32位的int值
我们假想一种情况:
有没有可能我们存储的值的key的hashcode大部分都是1在高位
例如:
1011 0000 … 0000
1000 0000 … 0000
0011 0000 … 0000
…
寻址算法又是取得hash值的低位数据 ===》 这导致一个结果就是高位数据集中在index=0;
这应该不会有人觉得这结果符合散列的要求吧?
为什么要>>>16?寻址算法导致的结果是index位置是hash值的低位决定,那高位岂不是没了参与感?
那就让高位也有参与感:
1、把hashcode右移16位,这样高位的16位数据平移到了低位;
2、高16位平移后和低16位做一次运算,这不就都参与了?
解决了>>>问题,那为什么要用^,而不是&或者|?
&:都为1则为1 ==》 有0就为0,结果更偏向于0
|:有1则为1 ==》 结果更偏向于1
^:相同为0,不同为1 ==》 …