解釋的很清楚!為什麼重寫equals()方法必須重寫hashcode()方法?

今天來點比較基礎的。

equals方法是Object類的方法,比較的是地址是否相等,跟==是等價的,註意,這說的是Object中是等價的。

public boolean equals(Object obj) { return (this == obj); }

但是,往往我們會重寫這個方法,比如判斷字符串是否相等啦

所以String重寫瞭equals()方法:

public boolean equals(Object anObject) {

if (this == anObject) {

return true;

}

if (anObject instanceof String) {

String anotherString = (String)anObject;

int n = value.length;

if (n == anotherString.value.length) {

char v1[] = value;

char v2[] = anotherString.value;

int i = 0;

while (n– != 0) {

if (v1[i] != v2[i])

return false;

i++;

}

return true;

}

}

return false;

}

可以看到比較的字符串內容是否相等,而不是地址瞭

再看hashcode方法也是object的方法,是一個native方法,看不到源碼,但是根據註釋可以知道它是根據對象的地址來算的,那麼不同對象的hashcode就不一樣瞭(極大的概率)

那為什麼重寫equals就要重寫hashcode呢?

假設String隻重寫瞭equals、沒重寫hashcode,即 下邊的ss1.equals(ss2)是true但是二者的hashcode不相等(地址不同)

String ss1=”mayijinfu”;

String ss2=”mayijinfu”;

現在我們在hashset插ss1和ss2,hashset會:

判斷插入的值的hashcode存不存在,插ss1判斷不存在,把ss1插入

插ss2判斷還是不存在(ss1,ss2的hashcode不相等,所以set不知道他兩的值一樣),這樣ss2也被插入瞭

這時我們遍歷set會發現存在兩個mayijinfu,這不就亂套瞭嗎。

歸根到底 很多容器先根據hashcode判斷存不存在如果hashcode存在再去根據equals判斷,

因為hashcode大概率不相等,所以可以少判斷一步equals

如果先判斷equals,大概率是不相等的,還要在判斷hashcode

所以我們重寫hashcode,不再根據地址來生成hashcode,而是根據我們自己規定的規則,比如上邊ss1和ss2,隻要內容一樣我們就讓他的hashcode一樣(String就是這麼辦的)

當我們重寫完hashcode後,ss1和ss2的hashcode就相等瞭,

現在我們在hashset插ss1和ss2,hashset會:

判斷插入的值的hashcode存不存在,插ss1時判斷這個hashcode不存在,把ss1插入

插ss2判斷它的hashcode存不存在,因為我們重寫瞭hashcode,所以已經存在瞭(ss1,ss2的hashcode相等,所以set知道瞭兩的值可能一樣),因為hashcode一樣瞭,set會再去進一步用equals判斷,ss1.equals(ss2) 返回true,那確定無疑他兩是同一個東西瞭(在我們的規則下)。

再來一個string的例子:

String的==比的是地址,地址相同才true;

String的equals()比的是每個字符;

String ss1=”mayijinfu”;

String ss2=”mayijinfu”;

String sn1= new String(“mayijinfu”);

String sn2=new String(“mayijinfu”);

System.out.println(“ss1==ss2:”+(ss1==ss2));

System.out.println(“ss1==sn1:”+(ss1==sn1));

System.out.println(“sn1==sn2:”+(sn1==sn2));

System.out.println(“ss1.equals(ss2):”+ss1.equals(ss2));

System.out.println(“ss1.equals(sn1):”+ss1.equals(sn1));

System.out.println(“sn1.equals(sn2):”+sn1.equals(sn2));

輸出結果:

ss1ss2:true

ss1sn1:false

sn1==sn2:false

ss1.equals(ss2):true

ss1.equals(sn1):true

sn1.equals(sn2):true

接下來舉一個例子:hashcode相同,但是內容不同 (真的存在)

下邊代碼中,p1和p2的hashcode相同,但是打印結果為true,false,”no boundaries”,null,也就是通過p2得不到p1的value,為啥呢?不是說Hashmap散列的時候看hashcode嗎?

計算索引是根據hashcode來算的,但是equals同樣要用到的。

String p1=”兒女”;

String p2 = “農豐”;

HashMap map = new HashMap();

map.put(p1,”no boundaries”);

System.out.println(“p1.hashCode()==p2.hashCode()”+(p1.hashCode()==p2.hashCode()));

System.out.println(“p1.equals(p2)”+p1.equals(p2));

System.out.println(“map.get(p1)”+map.get(p1));

System.out.println(“map.get(p2)”+map.get(p2));

hashmap的get方法依賴equals方法,上邊p1.equals(p2)是false,所以get到的是null啦。所以你想通過p2來get到p1的value,隻能重寫equals方法,但是問題又來瞭,String是final類,所以你隻能定義別的類瞭。

先定義一個類pp,然後重寫hashcode()和equals()方法,這裡hashcode其實就是他們值的hashcode,也就是String裡的hashcode,所以”兒女”和”農豐”的hashcode還是一樣的,然後我讓equals()方法的返回值跟hashcode()的返回值一致。

class pp {

String s;

public pp(String s){

this.s = s;

}

@Override

public boolean equals(Object obj){

return s.hashCode()==obj.hashCode();

}

@Override

public int hashCode(){

return s.hashCode();

}

}

java

pp p1 = new pp(“兒女”);

pp p2 = new pp(“農豐”);

HashMap map = new HashMap();

map.put(p1,”nb”);

System.out.println(“p1.hashCode()==p2.hashCode()”+(p1.hashCode()==p2.hashCode()));

System.out.println(“p1.equals(p2)”+p1.equals(p2));

System.out.println(“map.get(p1)”+map.get(p1));

System.out.println(“map.get(p2)”+map.get(p2));

這樣的話,p1和p2不光hashcode()相等,equals()也相等瞭,所以執行map.get(p2)得到的就是p1的value瞭。打印結果

p1.hashCode()==p2.hashCode()true

p1.equals(p2)true

map.get(p1)no boundaries

map.get(p2)no boundaries

解釋的很清楚!為什麼重寫equals()方法必須重寫hashcode()方法?

Published in News by Awesome.

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *