建議重寫equals方法時也一併重寫hashCode方法

weixin_34117211發表於2017-11-05

   Object類中有這樣一段說明,意思是建議我們equals方法和hashCode方法,或者一起重寫,或者一起不重寫,以維護hashCode的常規協定。

   什麼叫hashCode的常規協定呢?

   我的理解就是:兩個物件通過equals方法進行比較相等,它們分別呼叫hashCode方法時一定返回相同的整數;兩個物件通過equals方法比較不相等是,不要求它們呼叫hashCode方法時必須返回不同的值,但是程式設計師應該意識到,在這種情況下,讓它們返回不同的可以提高雜湊表的效能。

   怎麼又涉及到雜湊表的效能呢?

   其實,我們一般不會直接呼叫hashCode方法,但是在集合中它們會被頻繁的使用。重寫equals方法時建議重寫hashCode方法,是針對像Map、Set這種集合操作而言的。

   我們建立一個類,誰都不能保證它什麼時候會被放到集合中,所以,為了避免不必要的麻煩,建議重寫equals方法的同時也重寫hashCode方法,重寫hashCode方法的同時也一起重寫equals方法。

   其實,equals方法與hashCode方法不一致會帶來兩個比較明顯的問題。

   一、理解困難,容易混淆

   二、查詢效率低下,也就是所謂的雜湊表的效能會降低


   這又從何說起呢?

   我們知道,HashMap底層維護的是一個Entry型別的陣列,向HashMap中新增鍵值對時,首先通過Key值計算出一個位置,該位置可能就是此元素將要放置的位置。如果發現,這個位置上沒有元素,也就是為null,那麼就直接將這個待新增的元素放置在該位置上。如果這個位置上已經有元素存在,那麼接著通過equals方法比較這個待新增的元素Key與已經存在的這個元素的Key是否相等,不相等則執行新增操作,相等則返回舊值不執行新增操作。注意,這種情況下執行新增操作是將待新增的元素放置這個位置上,然後讓這個待新增的元素指向已經存在的這個舊元素,如此在這個位置上就形成了連結串列。同理,取出元素是和新增類似,是通過給定物件的hashCode()方法計算出一個位置,然後在該位置上進行查詢,找到就返回該元素,否則返回null。

   明白了這個過程就好辦了。

   如果equals方法與hashCode方法一致,那麼在HashMap所維護的那個Entry型別的陣列中的每個位置上就只有一個元素,而不會形成連結串列,這樣一來查詢的效能就非常好,同時理解上也十分清晰明瞭。反之,如果二者不一致,就有可能造成難以理解或者查詢的效能比較低。

下面以String類舉例說明:

  • 假設String類只重寫了equals()方法,而沒有重寫hashCode()方法。也就是說其hashCode()方法仍然繼承Object類的hashCode()方法。

    看下面的程式碼段

   HashSet set = new HashSet();

   String s1 = new String("hello");

   String s2 = new String("hello");

   set.add(s1);

   set.add(s2);

   System.out.println(set.size());    //輸出為2

   此時,set中有兩個元素,這兩個元素的內容都為"hello",它們儲存在陣列中的不同位置上,因為

   兩個物件的hashCode()返回值不同。

   如果我們將set中的元素迭代出來,就會看到兩個"hello"。這就造成了理解上困難了,因為在我

   們的記憶中set中的元素是不能重複的,但現在這個HashSet物件中卻有兩個"hello",這怎麼解釋

   呢?雖然,它們實際上是兩個不同的物件,但從表象上看它們確實是“一樣的”,也只有構造這        個HashSet物件的我們知道,但是其它呼叫者卻很難理解,這就是所謂的理解上的混亂。

  • 假如String類只重寫了hashCode()方法,而沒有重寫hashCode()方法。

    同樣,我們看上面那段程式碼

    set中仍然有兩個元素,但是相對於HashMap底層的那個Entry陣列來說,這兩個元素在相同的位置上,並且形成了連結串列,s2指向s1,因為s1和s2的hashCode()返回值相同,但通過equals()方法進行比較卻返回false。

    這樣一來,查詢起來就比較費勁了,因為要遍歷連結串列。

    我們知道,連結串列遍歷只能是通過前一個元素找下一個元素。

    我們這裡是兩個物件,找起來可能不那麼費力,但是物件多了呢?連結串列長了呢?

   這就是所謂的效率低。


這兩點就造成了我們前面提到的雜湊表的效能的下降。

   綜上所訴,雖然不強制要求重新equals方法時必須重寫hashCode方法或者是重寫hashCode方法必須重寫equals方法,但是為了避免不必要的麻煩,建議二者要麼一起重寫,要麼都不重寫,以保持它們的一致性。

   以上是作者的一點點理解,如有紕漏之處,還請多多指教。



本文轉自    手不要亂摸      51CTO部落格,原文連結:http://blog.51cto.com/5880861/1335139

   


相關文章