首先聲明自己大部分的理解的出處:如何重寫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 係數的問題的理解。