天天看點

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方法,則不一定要産生相同的整數結果。但是程式員應該知道,給不相等的對象産生截然不同的整數結果,有可能提高散清單的性能。