1. 程式人生 > >Java中==和equals的區別,equals和hashCode的區別

Java中==和equals的區別,equals和hashCode的區別

在java中:

==是運算子,用於比較兩個變數是否相等。

equals,是Objec類的方法,用於比較兩個物件是否相等,預設Object類的equals方法是比較兩個物件的地址,跟==的結果一樣。Object的equals方法如下:

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

hashCode也是Object類的一個方法。返回一個離散的int型整數。在集合類操作中使用,為了提高查詢速度。(HashMap,HashSet等)

有了這三個基礎概念,區別就簡單了。網上有很多,彙總一下:

java中的資料型別,可分為兩類: 
1.基本資料型別,也稱原始資料型別。byte,short,char,int,long,float,double,boolean 
  他們之間的比較,應用雙等號(==),比較的是他們的值。 
2.複合資料型別(類) 
  當他們用(==)進行比較的時候,比較的是他們在記憶體中的存放地址,所以,除非是同一個new出來的物件,他們的比較後的結果為true,否則比較後結果為false。 JAVA當中所有的類都是繼承於Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較物件的記憶體地 址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而不再是比較類在堆記憶體中的存放地址了。
  對於複合資料型別之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基於他們在記憶體中的存放位置的地址值的,因為Object的equals方法也是用雙等號(==)進行比較的,所以比較後的結果跟雙等號(==)的結果相同。


如果兩個物件根據equals()方法比較是相等的,那麼呼叫這兩個物件中任意一個物件的hashCode方法都必須產生同樣的整數結果。
如果兩個物件根據equals()方法比較是不相等的,那麼呼叫這兩個物件中任意一個物件的hashCode方法,則不一定要產生相同的整數結果

從而在集合操作的時候有如下規則:

將物件放入到集合中時,首先判斷要放入物件的hashcode值與集合中的任意一個元素的hashcode值是否相等,如果不相等直接將該物件放入集合中。如果hashcode值相等,然後再通過equals方法判斷要放入物件與集合中的任意一個物件是否相等,如果equals判斷不相等,直接將該元素放入到集合中,否則不放入。

回過來說get的時候,HashMap也先調key.hashCode()算出陣列下標,然後看equals如果是true就是找到了,所以就涉及了equals。

《Effective Java》書中有兩條是關於equals和hashCode的:

覆蓋equals時需要遵守的通用約定: 
  覆蓋equals方法看起來似乎很簡單,但是如果覆蓋不當會導致錯誤,並且後果相當嚴重。《Effective Java》一書中提到“最容易避免這類問題的辦法就是不覆蓋equals方法”,這句話貌似很搞笑,其實想想也不無道理,其實在這種情況下,類的每個例項都只與它自身相等。如果滿足了以下任何一個條件,這就正是所期望的結果: 
類的每個例項本質上都是唯一的。對於代表活動實體而不是值的類來說卻是如此,例如Thread。Object提供的equals實現對於這些類來說正是正確的行為。
不關心類是否提供了“邏輯相等”的測試功能。假如Random覆蓋了equals,以檢查兩個Random例項是否產生相同的隨機數序列,但是設計者並不認為客戶需要或者期望這樣的功能。在這樣的情況下,從Object繼承得到的equals實現已經足夠了。
超類已經覆蓋了equals,從超類繼承過來的行為對於子類也是合適的。大多數的Set實現都從AbstractSet繼承equals實現,List實現從AbstractList繼承equals實現,Map實現從AbstractMap繼承equals實現。
類是私有的或者是包級私有的,可以確定它的equals方法永遠不會被呼叫。在這種情況下,無疑是應該覆蓋equals方法的,以防止它被意外呼叫:
@Override 
public boolean equals(Object o){ 
  throw new AssertionError(); //Method is never called 


  在覆蓋equals方法的時候,你必須要遵守它的通用約定。下面是約定的內容,來自Object的規範[JavaSE6] 
自反性。對於任何非null的引用值x,x.equals(x)必須返回true。
對稱性。對於任何非null的引用值x和y,當且僅當y.equals(x)返回true時,x.equals(y)必須返回true
傳遞性。對於任何非null的引用值x、y和z,如果x.equals(y)返回true,並且y.equals(z)也返回true,那麼x.equals(z)也必須返回true。
一致性。對於任何非null的引用值x和y,只要equals的比較操作在物件中所用的資訊沒有被修改,多次呼叫該x.equals(y)就會一直地返回true,或者一致地返回false。
對於任何非null的引用值x,x.equals(null)必須返回false。

  結合以上要求,得出了以下實現高質量equals方法的訣竅: 
1.使用==符號檢查“引數是否為這個物件的引用”。如果是,則返回true。這只不過是一種效能優化,如果比較操作有可能很昂貴,就值得這麼做。 
2.使用instanceof操作符檢查“引數是否為正確的型別”。如果不是,則返回false。一般來說,所謂“正確的型別”是指equals方法所在的那個類。 
3.把引數轉換成正確的型別。因為轉換之前進行過instanceof測試,所以確保會成功。 
4.對於該類中的每個“關鍵”域,檢查引數中的域是否與該物件中對應的域相匹配。如果這些測試全部成功,則返回true;否則返回false。 
5.當編寫完成了equals方法之後,檢查“對稱性”、“傳遞性”、“一致性”。 


覆蓋equals時總要覆蓋hashCode 
  一個很常見的錯誤根源在於沒有覆蓋hashCode方法。在每個覆蓋了equals方法的類中,也必須覆蓋hashCode方法。如果不這樣做的話,就會違反Object.hashCode的通用約定,從而導致該類無法結合所有基於雜湊的集合一起正常運作,這樣的集合包括HashMap、HashSet和Hashtable。 
在應用程式的執行期間,只要物件的equals方法的比較操作所用到的資訊沒有被修改,那麼對這同一個物件呼叫多次,hashCode方法都必須始終如一地返回同一個整數。在同一個應用程式的多次執行過程中,每次執行所返回的整數可以不一致。
如果兩個物件根據equals()方法比較是相等的,那麼呼叫這兩個物件中任意一個物件的hashCode方法都必須產生同樣的整數結果。
如果兩個物件根據equals()方法比較是不相等的,那麼呼叫這兩個物件中任意一個物件的hashCode方法,則不一定要產生相同的整數結果。但是程式設計師應該知道,給不相等的物件產生截然不同的整數結果,有可能提高散列表的效能。