为什么重写 equals 时必须重写 hashCode ⽅法?
因为两个相等的对象的 hashCode
值必须是相等。也就是说如果 equals
方法判断两个对象是相等的,那这两个对象的 hashCode
值也要相等。
如果重写 equals()
时没有重写 hashCode()
方法的话就可能会导致 equals
方法判断是相等的两个对象,hashCode
值却不相等。
为什么两个相等的对象的 hashCode
值必须是相等?因为因为基于哈希的集合类(如 HashSet、HashMap、Hashtable 等)依赖于这一点来正确存储和检索对象。
比如HashSet
,当你把对象加入 HashSet
时,HashSet
会先计算对象的 hashCode
值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashCode
值作比较,如果没有相符的 hashCode
,HashSet
会假设对象没有重复出现。但是如果发现有相同 hashCode
值的对象,这时会调用 equals()
方法来检查 hashCode
相等的对象是否真的相同。如果两者相同,HashSet
就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals
的次数,相应就大大提高了执行速度。
也就是说:
- 如果两个对象的
hashCode
值相等,那这两个对象不一定相等(哈希碰撞)。 - 如果两个对象的
hashCode
值相等并且equals()
方法也返回true
,我们才认为这两个对象相等。 - 如果两个对象的
hashCode
值不相等,我们就可以直接认为这两个对象不相等。
问题来了?
怎么重写hashcode?
怎么重写才能保证如果 equals
方法判断两个对象是相等的,那这两个对象的 hashCode
值(哈希码)也要相等?
我们知道,object类的哈希码是由对象的内存地址得来的,现在重写了equal方法,一般是比较类的属性是否相同,所以,哈希码也应该和由属性(的哈希值)计算而来,我们一般是通过某个公式将所有属性带入计算得到哈希码。
例子:
package Test;
public class Person3 {
private String name;
private String idCard;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public Person3() {}
public Person3(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((idCard == null) ? 0 : idCard.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public static void main(String[] args) {
Person3 p1 = new Person3("赵四", "41081234");
Person3 p2 = new Person3("赵四", "41081234");
System.out.println("p1的hashCode是" + p1.hashCode());
System.out.println("p2的hashCode是" + p2.hashCode());
}
}
package Test;
public class Person3 {
private String name;
private String idCard;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public Person3() {}
public Person3(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((idCard == null) ? 0 : idCard.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public static void main(String[] args) {
Person3 p1 = new Person3("赵四", "41081234");
Person3 p2 = new Person3("赵四", "41081234");
System.out.println("p1的hashCode是" + p1.hashCode());
System.out.println("p2的hashCode是" + p2.hashCode());
}
}
这段代码的运行结果为:
p1的hashCode是1301683616
p2的hashCode是1301683616
在我们重写hashCode()方法后,可以看到两个属性值完全相同的对象,他们的哈希值是相同的。从业务的角度来说,达到了这两个对象相同的目的。