Java equals方法詳解

Object中的equals()

object中的的equals方法用於比較兩個物件是否相等,該方法原始碼如下:

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

物件均有記憶體地址和其具體內容,而Object中的equals方法是比較的兩個物件記憶體地址是否相同,即obj1。equals(obj2)為true,這表示兩者是引用同一個物件,但在實際開發中,大部分是兩個物件之間的比較,此時再用Object的equals方法就不行了,因此就要根據自己的需求重寫equals方法,java中的String類,Math類等均對equals方法進行了重寫

equals方法重寫遵循規則

自反性 :對於任何非空引用值 x,x。equals(x) 都應返回 true。

對稱性 : 對於任何非空引用值 x 和 y,當且僅當 y。equals(x) 返回 true 時,x。equals(y) 才應返回 true。

傳遞性:對於任何非空引用值 x、y 和 z,如果 x。equals(y) 返回 true,並且 y。equals(z) 返回 true,那麼 x。equals(z) 應返回 true。

一致性:對於任何非空引用值 x 和 y,多次呼叫 x。equals(y) 始終返回 true 或始終返回 false,前提是物件上 equals 比較中所用的資訊沒有被修改。

對於任何非空引用值 x,x。equals(null) 都應返回 false。

重寫equals方法實現

建立一個ObjectTest1類

/** * Created by lirui on 2018/12/12。 */public class ObjectTest1 { private Integer size; private String name; private String des; public Integer getSize() { return size; } public void setSize(Integer size) { this。size = size; } public String getName() { return name; } public void setName(String name) { this。name = name; } public String getDes() { return des; } public void setDes(String des) { this。des = des; } }

建立測試類

public class EqualsDemo { public static void main(String[] args) { ObjectTest1 objectTest1=new ObjectTest1(); objectTest1。setName(“wahh”); ObjectTest1 objectTest12=new ObjectTest1(); objectTest12。setName(“wahh”); System。out。println(“-=-=-=——=-=-=-=-=執行結果-=-=-=-=-=-=-=-=”); System。out。println(objectTest1。equals(objectTest12)); }}-=-=-=——=-=-=-=-=執行結果-=-=-=-=-=-=-=-=falseProcess finished with exit code 0

可以看到執行結果為false,因為沒有重寫equals方法,所以直接就會用Object類中的equals方法,比較兩者記憶體地址,因為是兩個物件,所以記憶體地址不一樣

重寫equals方法

/** * Created by lirui on 2018/12/12。 */public class ObjectTest1 { private Integer size; private String name; private String des; public Integer getSize() { return size; } public void setSize(Integer size) { this。size = size; } public String getName() { return name; } public void setName(String name) { this。name = name; } public String getDes() { return des; } public void setDes(String des) { this。des = des; } @Override public boolean equals(Object obj) { if (this==obj){ return true; } if (obj instanceof ObjectTest1){ ObjectTest1 objectTest1=(ObjectTest1) obj; return name。equals(objectTest1。name); }else{ return false; } } }

測試類結果

/** * Created by lirui on 2018/12/12。 */public class EqualsDemo { public static void main(String[] args) { ObjectTest1 objectTest1=new ObjectTest1(); objectTest1。setName(“wahh”); ObjectTest1 objectTest12=new ObjectTest1(); objectTest12。setName(“wahh”); System。out。println(“-=-=-=——=-=-=-=-=執行結果-=-=-=-=-=-=-=-=”); System。out。println(objectTest1。equals(objectTest12)); }}-=-=-=——=-=-=-=-=執行結果-=-=-=-=-=-=-=-=true

可以看到最後返回true,重寫equals方法首先判讀兩個物件記憶體地址是否相同,然後根據instanceof關鍵字判斷左邊物件是不是右邊的一個例項,如果是,最後判斷兩者name是否相同

在equals()中使用getClass進行型別判斷

建立 ObjectTestChild類繼承ObjectTest1

/** * Created by lirui on 2018/12/13。 */public class ObjectTestChild extends ObjectTest1{ String test; public ObjectTestChild(String name,String test) { super(name); this。test=test; } public String getTest() { return test; } public void setTest(String test) { this。test = test; } @Override public boolean equals(Object obj) { if (obj==this) return true; if (obj instanceof ObjectTestChild){ ObjectTestChild objectTestChild=(ObjectTestChild) obj; return super。equals(objectTestChild)&&objectTestChild。test==test; } return false; }}

上面的子類和父類都重寫了equals方法,子類只比父類多了一個test屬性,測試程式如下

public class EqualsDemo { public static void main(String[] args) { ObjectTest1 objectTest1 = new ObjectTest1(); objectTest1。setName(“wahaha”); ObjectTestChild child1 = new ObjectTestChild(“wahaha”, “123”); ObjectTestChild child2 = new ObjectTestChild(“wahaha”, “456”); System。out。println(“-=-=-=——=-=-=-=-=執行結果-=-=-=-=-=-=-=-=”); System。out。println(objectTest1。equals(child1)); System。out。println(objectTest1。equals(child2)); System。out。println(child1。equals(child2)); }}-=-=-=——=-=-=-=-=執行結果-=-=-=-=-=-=-=-=truetruefalse

上面定義了兩個子類和一個父類,兩個子類因為test不同,所以比較後為false是正常的,但服了分別和兩個子類比較均為true,這就和equals的傳遞性相違背;之所以會存在這個現象是因為我們使用了instanceof 關鍵字進行比較,因為子類繼承父類,所以左邊肯定等於右邊的例項,於此同時兩者name又相等,所以最好比較後相等,故在覆寫equals時推薦使用getClass進行型別判斷。而不是使用instanceof,getClass方法是返回執行時的類

改造父類和子類的equals重寫方法

父類 @Override public boolean equals(Object obj) { if (this==obj){ return true; } if (obj。getClass()==this。getClass()){ ObjectTest1 objectTest1=(ObjectTest1) obj; return name。equals(objectTest1。name); }else{ return false; } } 子類 public boolean equals(Object obj) { if (obj==this) return true; if (obj。getClass()==this。getClass()){ ObjectTestChild objectTestChild=(ObjectTestChild) obj; return super。equals(objectTestChild)&&objectTestChild。test==test; } return false; }

測試程式

public class EqualsDemo { public static void main(String[] args) { ObjectTest1 objectTest1 = new ObjectTest1(); objectTest1。setName(“wahaha”); ObjectTestChild child1 = new ObjectTestChild(“wahaha”, “123”); ObjectTestChild child2 = new ObjectTestChild(“wahaha”, “456”); System。out。println(“-=-=-=——=-=-=-=-=執行結果-=-=-=-=-=-=-=-=”); System。out。println(objectTest1。equals(child1)); System。out。println(objectTest1。equals(child2)); System。out。println(child1。equals(child2)); }}-=-=-=——=-=-=-=-=執行結果-=-=-=-=-=-=-=-=falsefalsefalse

重寫equals方法時為何要重寫hashCode方法

首先,equals方法與hashCode有如下的關係

如果兩個物件相同(即equals比較返回true),那麼兩者hashCode相同

如果兩個物件hashCode比較相同,那麼它們並不一定相同(即equals比較返回false)

為何重寫hashCode方法

由於為了提高程式的效率才實現了hashcode方法,先進行hashcode的比較,如果不同,那就沒必要在進行equals的比較了,這樣就大大減少了equals比較的次數,這對需要比較的數量很大時效率提高是很明顯的,一個很好的例子就是在集合中的使用;

我們都知道java中的List集合是有序的,因此是可以重複的,而set集合是無序的,因此是不能重複的,那麼怎麼能保證不能被放入重複的元素呢,單靠equals方法一樣比較的話,如果原來集合中有10000個元素了,那麼放入第10001個元素,難道要將前面的所有元素都進行比較,看看是否有重複,這個效率可想而知,因此hashcode就應運而生了,java就採用了hash表,利用雜湊演算法(也叫雜湊演算法),就是將物件資料根據該物件的特徵使用特定的演算法將其定義到一個地址上,那麼在後面定義進來的資料只要看對應的hashcode地址上是否有值,如果有值那麼就用equals比較,如果沒有則直接插入,這樣就大大減少了equals的使用次數,執行效率就大大提高了。

為什麼必須要重寫hashcode方法,其實簡單的說就是為了保證同一個物件,保證在equals相同的情況下hashcode值必定相同,如果重寫了equals而未重寫hashcode方法,可能就會出現兩個沒有關係的物件equals相同的(因為equal都是根據物件的特徵進行重寫的),但hashcode確是不相同的。