在hashCode()中,为什么一定要使用31来做乘法运算???

public native int hashCode();
作用:
​ 根据对象在堆内存中的首地址来生成一个哈希值,返回哈希值是一个int类型的整数(返回的结果可能是一个负数)。

问题:
​ 实例化出来的对象的地址值都不一样,则意味着实例化出来的对象调用hashCode()方法返回的结果都不相同。开发中,我们更多的是想通过对象的成员变量值来生成哈希值,那么该如何实现呢???

解决:
​ Object类提供的hashCode()方法满足不了我们的需求,那么我们在子类中就重写hashCode()方法即可。也就是在hashCode()方法中,根据对象的成员变量值来生成对象的哈希值,建议使用“alt + insert”快捷键来生成哈希值。

结论:
​ a)两个对象调用equals()方法返回的结果是true,则这两个对象调用hashCode()方法返回的结果相同。
​ –> 如果对象所对应的类没有重写equals()方法和hashCode()方法,则以上结论默认肯定满足。
​ –> 如果对象所对应的类重写了equals()方法和hashCode()方法,则以上结论也必须保证满足。
​ b)两个对象调用hashCode()方法返回的结果相同,则这两个对象调用equals()方法返回的结果未必是true。
​ –> 哈希算法是根据一定的规则来计算出来的结果,不同的两个对象得到哈希值可能相同,因此就有以上这个结论。

哈希算法的设计原则:不同对象计算出来的哈希值要尽可能的不同。

不同参数的hashCode代码如下:

1
2
3
4
5
6
7
8
9
10
11
public static int hashCode(Object a[]) {
if (a == null)
return 0;

int result = 1;

for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());

return result;
}

1
2
3
4
5
6
7
8
9
10
public static int hashCode(int a[]) {
if (a == null)
return 0;

int result = 1;
for (int element : a)
result = 31 * result + element;

return result;
}

1
2
3
4
5
6
7
8
9
10
public static int hashCode(boolean a[]) {
if (a == null)
return 0;

int result = 1;
for (boolean element : a)
result = 31 * result + (element ? 1231 : 1237);

return result;
}

还有很多不同参数的hash算法底层源码,再次不全部列举,但是可以看到都出现了31这个数字,为什么?

面试题1:在哈希算法中,为什么一定要使用31来做乘法运算???
a)因为31是一个质数,质数做乘法运算得到相同结果的概率较低。
​ 31*5 –结果–> 155 –得到155的方式–> 31*5、5*31、1*315
30*5 –结果–> 150 –得到150的方式–> 30*5、5*30、5*5*6、5*6*5、5*2*15等等

b)因为31是一个质数,质数做乘法运算的效率非常非常高(位运算)。
​ 公式:31*i 的位运算计算公式 –> (i << 5) - i
​ 例如:31*5 –套计算公式–> (5<<5)-5 –结果–> 155
​ 31*3 –套计算公式–> (3<<5)-3 –结果–> 93

面试题2:质数无穷无尽,为什么一定要选择31来做乘法运算???
​ 公式:7*i 的位运算计算公式 –> (i << 3) - i
​ 例如:7*5 –套计算公式–> (5<<3)-5 –结果–> 35
​ 答案:数学家让我们使用31来做乘法运算的。
总结:
对java提供的类而言,默认已经重写了equals()方法和hashCode()方法,我们直接使用即可。
对自定义的类而言,那么我们就需要手动的重写equals()方法和hashCode()方法,然后再去使用。