Java中如何判斷兩個對象是否相等(Java equals and ==)


原文https://www.dutycode.com/post-140.html

如何判斷兩個對象相等,這個問題實際上可以看做是如何對equals方法和hashcode方法的理解。

從以下幾個點來理解equals和hashCode方法:

1、equals的作用及與==的區別。
2、hashcode的作用及與equals的關系。
 
1、equals的作用及與==的區別。
equals被用來判斷兩個對象是否相等。
equals通常用來比較兩個對象的內容是否相等,==用來比較兩個對象的地址是否相等。
equals方法默認等同於“==”
Object類中的equals方法定義為判斷兩個對象的地址是否相等(可以理解成是否是同一個對象),地址相等則認為是對象相等。這也就意味着,我們新建的所有類如果沒有復寫equals方法,那么判斷兩個對象是否相等時就等同於“==”,也就是兩個對象的地址是否相等。
Object類中equals的方法實現如下:
 
public boolean equals(Object obj) {
        return (this == obj);
    }

  

所以可以總結為兩種情況但在我們的實際開發中,通常會認為兩個對象的內容相等時,則兩個對象相等,equals返回true。對象內容不同,則返回false。
1、類未復寫equals方法,則使用equals方法比較兩個對象時,相當於==比較,即兩個對象的地址是否相等。地址相等,返回true,地址不相等,返回false。
2、類復寫equals方法,比較兩個對象時,則走復寫之后的判斷方式。通常,我們會將equals復寫成:當兩個對象內容相同時,則equals返回true,內容不同時,返回false。
舉個例子:
public class EqualTest {
public static void main(String[] args) {
Person p1 = new Person(10,"張三");
Person p2 = new Person(10,"張三");
System.out.println(p1.equals(p2));
}
}
class Person{
int age;
String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

  


如果我們復寫equals方法呢?我們認為名字和年齡一樣的就是同一個人,那么p1和p2都表示10歲的張三,這兩個對象應該是相等的。復寫的equals方法如下:Person未復寫equals方法,則默認使用了Object中的equals,即為兩個對象(p1和p2)的內存地址判斷,p1和p2很明顯內存地址不同,所以輸出結果很明顯為false。
 
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

  


BTW:如果equals方法返回true,那么==是否也是true?同樣的,執行上述用例,得到的結果是true。
不一定是true。equals返回true有兩種可能,一種是兩個對象地址相同,一種是兩個對象內容相同。當內容相同時,地址可能不同,所以==比較的結果可能為false。
我們把main方法加上對==的判斷,如下:
 
public static void main(String[] args) {
Person p1 = new Person(10,"張三");
Person p2 = new Person(10,"張三");
System.out.println(p1.equals(p2));
System.out.println(p1 == p2);
}

  

 輸出結果很明顯 p1==p2的結果是false。
補充Java中對Equals的要求:
1. 對稱性:如果x.equals(y)返回是"true",那么y.equals(x)也應該返回是"true"。
2. 反射性:x.equals(x)必須返回是"true"。
3. 類推性:如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",那么z.equals(x)也應該返回是"true"。
4. 一致性:如果x.equals(y)返回是"true",只要x和y內容一直不變,不管你重復x.equals(y)多少次,返回都是"true"。
5. 非空性,x.equals(null),永遠返回是"false";x.equals(和x不同類型的對象)永遠返回是"false"。
 

  

2、hashCode的作用及與equals的關系。
hashCode的作用是用來獲取哈希碼,也可以稱作散列碼。實際返回值為一個int型數據。用於確定對象在哈希表中的位置。
Object中有hashcode方法,也就意味着所有的類都有hashCode方法。
但是,hashcode只有在創建某個類的散列表的時候才有用,需要根據hashcode值確認對象在散列表中的位置,但在其他情況下沒用。
java中本質上是散列表的類常見的有HashMap,HashSet,HashTable
所以,如果一個對象一定不會在散列表中使用,那么是沒有必要復寫hashCode方法的。但一般情況下我們還是會復寫hashCode方法,因為誰能保證這個對象不會出現再hashMap等中呢?
舉個例子:
兩個對象equals相等的時候,hashcode並不一定相等。
 
public class EqualTest {
public static void main(String[] args) {
Person p1 = new Person(10, "張三");
Person p2 = new Person(10, "張三");
System.out.println(
"p1.equals(p2)=" + p1.equals(p2) + ", p1.hashcode=" + p1.hashCode() + ", p2.hashcode=" + p2.hashCode());
}
}
class Person {
int age;
String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}

  

 Person沒有復寫hashCode方法,使用Object默認的hashCode實現,輸出結果如下:
p1.equals(p2)=true, p1.hashcode=246688959, p2.hashcode=1457895203

 從結果可以看出,equals雖然相同,但是p1和p2的hashcode並不相同。
如果Person用於散列表的類中呢,這里用HashSet來做測試。

public class EqualTest {
public static void main(String[] args) {
Person p1 = new Person(10, "張三");
Person p2 = new Person(10, "張三");
System.out.println(
"p1.equals(p2)=" + p1.equals(p2) + ", p1.hashcode=" + p1.hashCode() + ", p2.hashcode=" + p2.hashCode());
HashSet<Person> set = new HashSet<Person>();
set.add(p1);
set.add(p2);
System.out.println(set);
}
}
class Person {
int age;
String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}
輸出結果 

p1.equals(p2)=true, p1.hashcode=246688959, p2.hashcode=1457895203
[Person [age=10, name=張三], Person [age=10, name=張三]]

  

p1和p2的equals相同,我們認為是兩個對象相等,但是這兩個對象竟然同時出現再hashSet中,hashSet中是不會出現兩個相同的元素的。  
那問題在哪里?
hashset在添加一個元素的時候,會做如下判斷:
1、如果添加元素的hashcode相等並且 對象equals為true或對象== 時,則認為是同一個元素,不添加到新元素中。
2、如果不符合上述條件,則認為是一個新元素,添加到set中。
所以,雖然p1和p2equals比較時相等,但是hashcode並不一樣,所以在往set中添加的時候認為是兩個不同的元素,所以才會出現了p1和p2同時在set中的情況。
 
我們改進下,復寫一下hashcode方法,如下:
 
class Person {
int age;
String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}

  

重新執行結果: 
 
p1.equals(p2)=true, p1.hashcode=776160, p2.hashcode=776160
[Person [age=10, name=張三]]

  

於是看到set中僅有一個Person值了。 
 
補充幾點:
1、新建一個類,尤其是業務相關的對象類的時候,最好復寫equals方法。
2、復寫equals方法時,同時記着要復寫hashCode方法,誰能保證說這個對象一定不會出現在hashMap中呢?如果你用的是eclipse的自動代碼生成,你會發現eclipse中復寫equals和hashCode是在一起的。
 
引申出幾個經常在面試中問到的問題:
     1、兩個對象,如果a.equals(b)==true,那么a和b是否相等?
          相等,但地址不一定相等。
     2、兩個對象,如果hashcode一樣,那么兩個對象是否相等?
          不一定相等,判斷兩個對象是否相等,需要判斷equals是否為true。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM