重寫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 係數的問題的理解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章