为什么我需要重写Java中的equals和hashCode方法?

最近我通读了这篇文章
开发者工作文档

本文档的全部内容都是关于有效和正确地定义hashCode()equals()的,但是我无法理解为什么我们需要覆盖这两个方法

我如何才能做出有效实施这些方法的决定

约书亚·布洛赫说有效的Java

必须在每个重写equals()的类中重写hashCode()。否则将导致违反Object.hashCode()的一般约定,这将阻止类与所有基于哈希的集合(包括HashMap、HashSet和Hashtable)一起正常工作

让我们试着用一个例子来理解它,如果我们重写equals(),而不重写hashCode(),并尝试使用Map,会发生什么

假设我们有这样一个类,MyClass的两个对象是相等的,如果它们的importantField相等(由eclipse生成的hashCode()equals()

公共类MyClass{
私有最终字符串重要字段;
私有最终字符串另一个字段;
公共MyClass(最终字符串相等字段,最终字符串另一个字段){
this.importantField=equalField;
this.anotherField=另一个字段;
}
@凌驾
公共int hashCode(){
最终整数素数=31;
int结果=1;
结果=素数*结果
+((importantField==null)?0:importantField.hashCode());
返回结果;
}
@凌驾
公共布尔等于(最终对象obj){
if(this==obj)
返回true;
if(obj==null)
返回false;
如果(getClass()!=obj.getClass())
返回false;
最终MyClass other=(MyClass)obj;
if(importantField==null){
if(other.importantField!=null)
返回false;
}如果(!importantField.equals(other.importantField))
返回false;
返回true;
}
}

想象一下你有这个

MyClass first=新的MyClass(“a”和“first”);
MyClass second=新的MyClass(“a”、“second”);

仅覆盖等于

如果只覆盖equals,那么当您调用myMap.put(第一个,someValue)时,第一个将散列到某个bucket,当您调用myMap.put(第二个,someOtherValue)时,它将散列到另一个bucket(因为它们具有不同的hashCode)。因此,尽管它们是相等的,因为它们不会散列到同一个桶中,映射无法实现这一点,并且它们都留在映射中


虽然如果我们重写hashCode(),没有必要重写equals(),但是让我们看看在这种特殊情况下会发生什么,我们知道MyClass的两个对象如果其重要字段相等,那么它们是相等的,但我们不重写equals()

仅覆盖hashCode

如果您只覆盖hashCode,那么当您调用myMap.put(first,someValue)时,它首先计算其hashCode,并将其存储在给定的存储桶中。然后,当您调用myMap.put(second,someOtherValue)时,它应该按照映射文档,因为它们相等(根据业务需求)

但问题是equals没有被重新定义,因此当映射散列second并在bucket中迭代查找是否存在对象k,从而second.equals(k)为真时,它将找不到任何assecond。equals(first)将为false

希望是清楚的

发表评论