本文地址:https://www.dutycode.com/post-140.html
除非注明,文章均為 www.dutycode.com 原創,歡迎轉載!轉載請注明本文地址,謝謝。
如何判斷兩個對象相等,這個問題實際上可以看做是如何對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; }
}
|
Person未復寫equals方法,則默認使用了Object中的equals,即為兩個對象(p1和p2)的內存地址判斷,p1和p2很明顯內存地址不同,所以輸出結果很明顯為false。
如果我們復寫equals方法呢?我們認為名字和年齡一樣的就是同一個人,那么p1和p2都表示10歲的張三,這兩個對象應該是相等的。復寫的equals方法如下:
@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;
}
|
同樣的,執行上述用例,得到的結果是true。
BTW:如果equals方法返回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。