在上篇文章中,我们提到了HashMap在添加元素时取得hashCode的算法。
- 得到key值的hashCode();
- 做异或操作;
- 取模运算;
第一步很好理解,也就是调用key.hashCode()这个算法,取得其原始的哈希值。
第二步,首先根据传入的key值生成一个哈希值,随后将该哈希值取高16位后与其本身进行异或运算。这个运算本身只是一个扰动,是为了让元素分布更加均匀,尽量减少哈希冲突。同时,让高16位与低16位进行异或,那么他们都参与到了扰动当中,变相保留了原本高位数据特征的同时增加了低位的随机性。
第三步,进行取模运算。这里就是进行性能优化的一个关键。
当时,
那这个是怎么来的呢?
假设,我们要计算的:
- 的二进制为(假设是8位二进制,实际在java中int是32位,这里为了方便起见就不再赘述)。那么如果一个数的二进制比大,那么其对取余数时,二进制中比1000高的高位就应该全部置0。类比十进制就很容易想通,对于十进制数来说,如果一个数比如1234对10取余数,那么只需要 令,此时a的余数就是4,相当于把123这高三位全部置0。同理,二进制为1010,如果对二进制的1000取余,那就是把高1位置0。
- 那么知道了这个之后再看,为什么直接与取与就能够得到余数。已知为,那么的二进制就必然为高1位为1,其余为0的形式。如:那么也就会是高1位为0,其余为1的形式。如: 显然,将与原先的数相&,就可以直接将所需置0的位置置零,最后所得到的就是余数。
这样一来就将取模的操作转换成了相与,效率得到了很大程度的提升。