java對hashCode()和equals()方法的探討
我們經常聽說過如果我們重寫的equals()方法,那麼我們必須重新hashCode()方法,那麼為什麼要這麼做呢,其實其中還是有原因的
我們可以舉一個例子,可以利用HashSet來檢驗,我們知道HashSet中定義了一個HashMap的變數和一個final型別的Object型別常量,我們每次在HashSet中新增元素時,都是將這個元素作為鍵,將final型別的Object類常量作為值存放到HashMap中的,也就是說,我們取元素,刪除元素,插入元素都是基於HashMap的,這也就保證了對於插入元素時,Hashset裡的元素永遠都不會重複,因為HashMap的鍵不可能重複
如果不懂HashMap的資料結構,其實可以看我的另一篇部落格,裡面進行了部分的講解
裡面講的也比較細,這裡也就不做過多的敘述,當我們通過元素的hashcode來計算出元素在陣列table中的索引,如果陣列的索引相等,然後就會呼叫equals方法來進行比較兩個key是否相等,如果相等就會覆蓋,如果不相等就會將這個key放置到Entry鏈的頭部,其實對HashSet的操作我們完全可以看作是對HashMap的操作,那麼看下面的例子:
上面的例子中我主要定義了幾個類,分別是A,B,C類,A類重寫了hashCode()方法,但沒有重寫equals()方法,B類重寫了equals()方法,但沒有重寫hashCode()方法,C類重寫了hashCode()方法和equals()方法,然後我們執行程式:import java.util.HashSet; import java.util.Iterator; class A{ @Override public int hashCode(){ return 1; } } class B{ @Override public boolean equals(Object o){ return true; } } class C{ @Override public int hashCode(){ return 2; } @Override public boolean equals(Object o){ return true; } } public class Human { public static void main(String[] args){ HashSet<Object> set=new HashSet<Object>(); set.add(new A()); set.add(new A()); set.add(new B()); set.add(new B()); set.add(new C()); set.add(new C()); Iterator<Object> iterator=set.iterator(); while(iterator.hasNext()){ //列印物件地址 System.out.println(iterator.next()); } } }
得到的結果是:
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
上面發現A和B都出現了兩次,而C只出現了一次,那麼為什麼呢,這裡如果知道HashMap的思想,就會非常容易理解
首先A重寫了hashCode()方法,那麼HashMap在插入值得時候就會通過這個hashCode來判斷table陣列index的位置,當插入兩個A的例項時,其實他們在陣列的索引是相同的,但是由於我們例項化兩個物件A,他們在堆記憶體中的地址不同,呼叫的是Object類的equals方法,所以最後得到的結果肯定兩個不同物件,返回的肯定是false,那麼就會放在同一個索引index中Entry鏈下,但是插入兩個物件B時,我們重寫了equals()方法,首先會計算hash值時,確定放入兩個不同的索引下(因為兩個物件hashcode肯定不同,其實hashcode完全可以看成地址),但是即使equals返回true,在自身的索引下的Entry鏈中並沒有找到相同的元素,但是對於C物件,hashCode和equals方法都相同,所以在放入同一索引下時判斷的equals方法也是true,那麼最後的結果就認為是同一個物件,所以就把之前的key給覆蓋了,所以最後的結果就會有兩個A,並且是連在一起的,因為他們在同一索引的Entry鏈上,有兩個B,他們的索引不同,一個C,並且覆蓋了之前的C,
修改類A的hashCode返回值為2,可以猜猜是什麼結果?
最後的結果就會是兩個A,兩個B,沒有C,道理差不多也是一樣的,因為C中定義的equals方法引數是Object型別,就會和所有的類比較都是返回true,那麼A也不例外
所以最後我們可以得出結論,就是,如果我們利用一個自定義物件的例項來作為HashMap的鍵時,我們如果想讓兩個物件在內容相同的情況下,就判斷是同一個物件,在HashMap的鍵中只出現一次,那麼我們必須要重寫hashCode方法,保證最後在同一個索引處,同時也要重寫equals方法,保證在內容相同的情況下就返回true,這樣就能保證在HashMap中只出現一次了,只重寫其中一個方法,一般最後的結果都會出現兩次