为什么我需要重写Java中的equals和hashCode方法?
最近我读了这个开发工程文件。
该文档全部关于有效且正确地定义hashCode()
和equals()
,但我无法弄清楚为什么我们需要重写这两个方法。
我怎样才能做出有效实施这些方法的决定?
Joshua Bloch谈到Effective Java
您必须在覆盖equals()的每个类中重写hashCode()。 如果不这样做将违反Object.hashCode()的一般合约,这会阻止您的类与所有基于散列的集合(包括HashMap,HashSet和Hashtable)一起正常运行。
让我们尝试用一个例子来理解它,如果我们重写equals()
而不重写hashCode()
并尝试使用Map
,会发生什么。
假设我们有这样的类,并且MyClass
两个对象如果它们的importantField
相等(用eclipse生成的hashCode()
和equals()
hashCode()
,它们是相等的)
public class MyClass {
private final String importantField;
private final String anotherField;
public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}
public String getEqualField() {
return importantField;
}
public String getAnotherField() {
return anotherField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}
重写仅equals
如果只有equals
被覆盖,那么当你调用myMap.put(first,someValue)
首先散列到某个存储桶中,而当你调用myMap.put(second,someOtherValue)
它会散列到其他存储桶(因为它们具有不同的hashCode
)。 所以,尽管它们是相同的,但是它们不会散列到同一个桶中,所以地图无法实现,并且它们都留在地图中。
虽然如果我们重写hashCode()
,不需要重写equals()
,但让我们看看在这种特殊情况下会发生什么,我们知道MyClass
两个对象如果它们的importantField
相等,但我们不覆盖equals()
。
只覆盖hashCode
想象一下你有这个
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
如果你只重写hashCode
那么当你调用myMap.put(first,someValue)
时,首先需要计算它的hashCode
并将其存储在给定的桶中。 然后,当你调用myMap.put(second,someOtherValue)
它应该根据Map文档第一个和第二个替换,因为它们是相等的(根据业务需求)。
但是问题在于equals没有被重新定义,所以当map second
迭代并遍历桶时,看看是否存在一个使得second.equals(k)
为true的对象k
,它将不会找到任何作为second.equals(first)
将是false
。
希望很明显
像HashMap和HashSet这样的集合使用对象的散列码值来确定对象应该如何存储在集合中,并再次使用散列码来帮助定位集合中的对象。
散列检索是一个两步过程。
这里是一个小例子,为什么我们应该忽略equals()和hashcode()。考虑一个Employee类,它有两个字段age和name。
public class Employee {
String name;
int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof Employee))
return false;
Employee employee = (Employee) obj;
return employee.getAge() == this.getAge()
&& employee.getName() == this.getName();
}
// commented
/* @Override
public int hashCode() {
int result=17;
result=31*result+age;
result=31*result+(name!=null ? name.hashCode():0);
return result;
}
*/
}
现在创建一个类,将Employee对象插入HashSet并测试该对象是否存在。
public class ClientTest {
public static void main(String[] args) {
Employee employee = new Employee("rajeev", 24);
Employee employee1 = new Employee("rajeev", 25);
Employee employee2 = new Employee("rajeev", 24);
HashSet<Employee> employees = new HashSet<Employee>();
employees.add(employee);
System.out.println(employees.contains(employee2));
System.out.println("employee.hashCode(): " + employee.hashCode()
+ " employee2.hashCode():" + employee2.hashCode());
}
}
它会打印
false
employee.hashCode(): 321755204 employee2.hashCode():375890482
现在从hashcode()中移除注释行并执行,现在它将打印
true
employee.hashCode(): -938387308 employee2.hashCode():-938387308
现在你能明白为什么如果两个对象被认为是相等的,他们的hashcode也必须相等? 否则,你永远无法找到该对象,因为在Object类中的默认hashcode方法实际上总是为每个对象都提供一个唯一的编号,即使equals()方法被重写以至于两个或多个对象被认为是平等的。 如果它们的哈希码不能反映这些对象,它们的平等程度如何。 再来一次:如果两个对象相等,那么它们的哈希码也必须相等。
您必须在覆盖equals()的每个类中重写hashCode()。 如果不这样做将违反Object.hashCode()的一般合约,这会阻止您的类与所有基于散列的集合(包括HashMap,HashSet和Hashtable)一起正常运行。
来自Effective Java,Joshua Bloch
通过一致地定义equals()
和hashCode()
,可以提高类的可用性作为基于散列的集合中的键。 正如hashCode的API文档所解释的那样:“为了散列表的好处而支持此方法,例如java.util.Hashtable
提供的方法。”
关于如何高效地实现这些方法的问题,最好的答案是建议您阅读Effective Java的第3章。
链接地址: http://www.djcxy.com/p/76107.html上一篇: Why do I need to override the equals and hashCode methods in Java?
下一篇: Why do we need synchronized ArrayLists when we already have Vectors?