建議重寫equals方法時也一併重寫hashCode方法
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
相關文章
- 重寫equals()方法時,需要同時重寫hashCode()方法
- 重寫equals()時為什麼也得重寫hashCode()之深度解讀equals方法與hashCode方法淵源
- Java重寫equals方法時為什麼要重寫hashcode方法Java
- 為什麼重寫 equals() 方法,一定要重寫 hashCode() 呢?| HashMapHashMap
- 重寫Object.equals()方法和Object.hashCode()方法Object
- java為什麼要重寫hashCode和equals方法Java
- 為什麼重寫equals必須重寫hashCode
- java 中為什麼重寫 equals 後需要重寫 hashCodeJava
- 關於重寫equals()和hashCode()的思考
- hashcode重寫
- 關於HashMap的key重寫hashcode和equals的理解HashMap
- 方法重寫(Override)IDE
- java 方法重寫概念Java
- java之方法的重寫Java
- HashSet中重寫haseCode和equals
- 細說equals()方法和hashCode()方法
- 詳解equals()方法和hashCode()方法
- Java中方法重寫與方法過載Java
- java方法的神奇修改(重寫)Java
- 【iOS】category重寫方法的呼叫iOSGo
- 搞懂 Java equals 和 hashCode 方法Java
- JavaBean重寫Object類中的方法JavaBeanObject
- 方法重置和重寫的區別
- Java基礎系列-equals方法和hashCode方法Java
- String的equals和hashCode方法
- 自動生成hashcode和equals方法
- Java 面試題關於方法的重寫Java面試題
- 複習java面對物件(方法重寫)Java物件
- python繼承和重寫init方法Python繼承
- 07-08、構造方法基本概念:重寫init方法構造方法
- PHP 方法重寫,引數不同,報錯: DeclarationPHP
- java 新建立的類要重寫的方法Java
- 第9條:覆蓋equals時總要覆蓋hashCode方法
- 物件只定義了Equals和Hashcode方法之一的漏洞物件
- 重寫JS中的apply,call,bind,new方法JSAPP
- python繼承和重寫init方法--例項Python繼承
- 重寫Java的String及其大部分方法Java
- 重寫陣列的方法(改變原陣列)陣列