重写hashcode的原因 以及为啥用31的个人理解

首先声明自己大部分的理解的出处:如何重写hashCode()和equals()方法

接下来自己的理解:
1、首先java中set 、HashMap貌似包括List等底层的存储都会把,存储区域分成n个部分,而具体存在哪个部分是由hashcode决定的,也就是说查询的时候他会通过hashcode 所有小查询范围,所以如果所有的hashcode都一样,你的hashcode返回了一个常量 ,那么结果就是存储进去以后 都存放在一个区域,查询的时候变成了一个链式查询,完全没有效率。
2、如果你的hashcode返回的是一个随机数,或者不去重写hashcode,那么即使两个对象是一样的,也会出现问题。举个例子(应用自上面的博客):
我们先创建2个新的Coder对象:

Coder c1 = new Coder("bruce", 10);  
02.        Coder c2 = new Coder("bruce", 10);  

假定我们已经重写了Coder的equals()方法而没有重写hashCode()方法:

@Override  
02.    public boolean equals(Object other) {  
03.        System.out.println("equals method invoked!");  
04.          
05.        if(other == this)  
06.            return true;  
07.        if(!(other instanceof Coder))  
08.            return false;  
09.          
10.        Coder o = (Coder)other;  
11.        return o.name.equals(name) && o.age == age;  
12.    }  

然后我们构造一个HashSet,将c1对象放入到set中:

Set<Coder> set = new HashSet<Coder>();  
 set.add(c1); 

最后:

System.out.println(set.contains(c2));  

我们期望contains(c2)方法返回true, 但实际上它返回了false.

c1和c2的name和age都是相同的,为什么我把c1放到HashSet中后,再调用contains(c2)却返回false呢?这就是hashCode()在作怪了.因为你没有重写hashCode()方法,所以HashSet在查找c2时,会在不同的bucket中查找.比如c1放到05这个bucket中了,在查找c2时却在06这个bucket中找,这样当然找不到了.因此,我们重写hashCode()的目的在于,在A.equals(B)返回true的情况下,A, B 的hashCode()要返回相同的值.

接下来就是重写hashcode的时候会用31这个数 一般写法:

@Override  
02.    public int hashCode() {  
03.        int result = 17;  
04.        result = result * 31 + name.hashCode();  
05.        result = result * 31 + age;  
06.          
07.        return result;  
08.    }  

我的理解是:首先为了尽量让产生hashcode保持唯一,所以一定使用一个素数来做系数(这里的31),但为什么是31而不是别的素数呢,因为:
31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化,使用31的原因可能是为了更好的分配hash地址,并且31只占用5bits!
所以从效率上 它是2的5次减1,对计算机来说2的乘除操作只需要做位移操作,例如*32就是左移5位。
也就是说31对计算机的角度来说运算更快、切占内存不多不少,而且形成惯例,虚拟机甚至都专门对他做了优化。所以常用31做系数算hashcode
以上理解来源于关于hashcode 里面 使用31 系数的问题的理解。

展开阅读全文

没有更多推荐了,返回首页