為什么equals()方法要重寫?
判斷兩個對象在邏輯上是否相等,如根據類的成員變量來判斷兩個類的實例是否相等,而繼承Object中的equals方法只能判斷兩個引用變量是否是同一個對象。這樣我們往往需要重寫equals()方法。
我們向一個沒有重復對象的集合中添加元素時,集合中存放的往往是對象,我們需要先判斷集合中是否存在已知對象,這樣就必須重寫equals方法。
怎樣重寫equals()方法?
重寫equals方法的要求:
1、自反性:對於任何非空引用x,x.equals(x)應該返回true。
2、對稱性:對於任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也應該返回true。
3、傳遞性:對於任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也應該返回true。
4、一致性:如果x和y引用的對象沒有發生變化,那么反復調用x.equals(y)應該返回同樣的結果。
5、非空性:對於任意非空引用x,x.equals(null)應該返回false。
1、自反性原則
在JavaBean中,經常會覆寫equals方法,從而根據實際業務情況來判斷兩個對象是否相等,比如我們寫一個person類,根據姓名來判斷兩個person類實例對象是否相等。代碼如下:
public class Person {
private String name;
public Person(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
Person person= (Person) obj;
return name.equalsIgnoreCase(person.getName().trim());
}
return false;
}
public static void main(String[] args){
Person p1=new Person("張三");
Person p2=new Person("張三 ");
List<Person> list = new ArrayList<Person>();
list.add(p1);
list.add(p2);
System.out.println("是否包含張三:"+list.contains(p1));
System.out.println("是否包含張三:"+list.contains(p2));
}
}
list中含有這個生成的person對象,結果應該為true,但是實際結果: 這里考慮了字符串空格的問題,去除前后的空格。
是否包含張三:true
是否包含張三:false
第二個為什么會是false呢?原因在於list中檢查是否含有元素時是通過調用對象的equals方法來判斷的,也就是說 contains(p2)傳遞進去會依次執行p2.equals(p1)、p2.equals(p2),只要一個返回true,結果就是true。但是這里p2.equals(p2)返回的是false?由於我們對字符前后進行了空格的切割造成p2.equals(p2)的比較實際上是:“張三 ”.equals(“張三”),一個有空格,一個沒有空格就出錯了。
這個違背了equals的自反性原則:對於任何非空引用x,x.equals(x)應該返回true。
這里只要去掉trim方法就可以解決。
2、對稱性原則
上面這個例子,還並不是很好,如果我們傳入null值,會怎么樣呢?增加一條語句:Person p2=new Person(null);
結果:
1
2
|
是否包含張三: true
Exception in thread "main" java.lang.NullPointerException
|
原因在執行p2.equals(p1)時,由於p2的name是一個null值,所以調用name.equalsIgnoreCase()方法時就會報空指針異常。
這是在覆寫equals方法時沒有遵循對稱性原則:對於任何應用x,y的情形,如果想x.equals(y)返回true,那么y.equals(x),也應該返回true。
應該在equals方法里加上是否為null值的判斷:
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
Person person= (Person) obj;
if (person.getName() == null || name == null) {
return false;
}else{
return name.equalsIgnoreCase(person.getName());
}
}
return false;
}
3、傳遞性原則
現在我們有一個Employee類繼承自person類:
public class Employee extends Person{
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Employee(String name,int id) {
super(name);
this.id = id;
// TODO Auto-generated constructor stub
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Employee){
Employee e = (Employee)obj;
return super.equals(obj) && e.getId() == id;
}
return super.equals(obj);
}
public static void main(String[] args){
Employee e1=new Employee("張三",12);
Employee e2=new Employee("張三",123);
Person p1 = new Person("張三");
System.out.println(p1.equals(e1));
System.out.println(p1.equals(e2));
System.out.println(e1.equals(e2));
}
}
只有在name和ID都相同的情況下才是同一個員工,避免同名同姓的。在main里定義了,兩個員工和一個社會閑雜人員,雖然同名同姓但肯定不是同一個人。運行結果應該三個都是false才對。但是:
true
true
false
p1盡然等於e1,也等於e2,不是同一個類的實例也相等了?因為p1.equals(e1)是調用父類的equals方法進行判斷的它使用instanceof關鍵字檢查e1是否是person的實例,由於employee和person是繼承關系,結果就是true了。但是放過來就不成立,e1,e2就不等於p1,這也是違反對稱性原則的一個典型案例。
e1竟然不等於e2?e1.equals(e2)調用的是Employee的equals方法,不僅要判斷姓名相同還有判斷工號相同,兩者的工號不同,不相等時對的。但是p1等於e1,也等於e2,e1卻不等於e2,這里就存在矛盾,等式不傳遞是因為違反了equals的傳遞性原則:對於實例對象x、y、z;如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也應該返回true。
上述情況會發生是因為父類使用instanceof關鍵字(是否是這個特定類或者是它的子類的一個實例),用來判斷是否是一個類的實例對象的,這很容易讓子類“鑽空子”。想要解決也很簡單,使用getClass進行類型的判斷,person類的equals方法修改如下:
@Override
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == this.getClass()) {
Person person= (Person) obj;
if (person.getName() == null || name == null) {
return false;
}else{
return name.equalsIgnoreCase(person.getName());
}
}
return false;
}
4、必須覆寫hashCode方法這樣結果就是三個false。
覆寫equals方法就必須覆寫hashCode方法,這是Javaer都知道的。原因就是HashMap的底層處理機制是以數組的方式保存map條目的,這其中的關鍵是這個數組下標的處理機制:依據傳入元素的hashCode方法的返回值決定其數組的下標,如果該數組位置上已經有了map條目,且與傳入的鍵值相等則不處理,若不相等則覆蓋;如果數組位置沒有條目,則插入,並加入到map條目的鏈表中。同理檢查鍵是否存在也是根據哈希嗎確定文職,然后遍歷查找鍵值的。
那么對象的hashCode方法返回的是什么呢?他是一個對象的哈希碼,是有Object類的本地方法生成的,確保每個對象有一個哈希碼。
具體的可以看另一篇博文:http://www.cnblogs.com/silence-hust/p/4510574.html