关于equals和hashCode,有这样两个规范:
(1)如果两个对象的equals的结果是相等的,则两个对象的hashCode的返回结果也必须是相同的。
(2)任何时候重写equals,都必须同时重写hashCode。
为什么说任何时候重写equals,都必须同时重写hashCode?如果不重写hashCode会怎样?下面来试验一下:
(1)定义一个对象EqualsObject,重写equals()方法(不重写hashCode):
public class EqualsObject {
private int id;
private String name;
@Override
public boolean equals(Object obj) {
//如果为null或者不是一个类型,直接返回false
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
//如果引用指向同一个对象,则返回true
if (this == obj) {
return true;
}
//强制转换为比较类型
EqualsObject temp = (EqualsObject) obj;
//判断属性值是否相等
if (temp.getId() == this.id && name.equals(temp.getName())) {
return true;
}
return false;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试:
public static void main(String[] args) {
Set<EqualsObject> set = new HashSet<>();
EqualsObject A = new EqualsObject(1, "CHD");
EqualsObject B = new EqualsObject(1, "CHD");
EqualsObject C = new EqualsObject(1, "CHD");
set.add(A);
set.add(B);
set.add(C);
System.out.println(A.hashCode());
System.out.println(B.hashCode());
System.out.println(C.hashCode());
System.out.println(set.size());
}
测试结果:
分析:
A、B、C这三个对象显而易见是相同的,应该输出1才对,为什么会输出3呢?
这是因为如果不重写 hashCode(),即使equals() 相等也毫无意义,Object.hashCode() 的实现是默认为每一个对象生成不同的int数值,它本身是一个native方法,一般与对象的内存地址有关。hashCode是根据对象的地址进行相关计算得到int类型数值的。
因为EqualsObject没有重写hashCode,所以得到的是一个与对象地址相关的唯一值。
现在我们重写hashCode():
(1)这个是使用IDEA自动生成的:
@Override
public int hashCode() {
return Objects.hash(id, name);
}
这个方法使用了 Java 标准库中的 Objects.hash()
静态方法,它接收多个参数,并根据这些参数计算出一个哈希码。这种实现方式更可靠,它会保证在计算哈希码时考虑了所有传入的参数,确保了哈希码的分布更均匀,减少了哈希冲突的可能性。
(2)这个是我们自己编写的:
@Override
public int hashCode() {
return id + name.hashCode(); //name是String类型,String重写了hashCode()
}
这个方法的计算方式是将对象的 id
(假设是一个整数)与 name
字符串的哈希码相加。这样的实现可能会导致哈希码的分布不均匀,从而降低哈希表的性能。特别是当 id
和 name
的范围很大时,相加后的结果可能会导致哈希码冲突较多,从而导致哈希表的查找效率下降。
这两种方式都可以用,现在测试:
测试代码:
public static void main(String[] args) {
Set<EqualsObject> set = new HashSet<>();
EqualsObject A = new EqualsObject(1, "CHD");
EqualsObject B = new EqualsObject(1, "CHD");
EqualsObject C = new EqualsObject(1, "CHD");
set.add(A);
set.add(B);
set.add(C);
System.out.println(A.hashCode());
System.out.println(B.hashCode());
System.out.println(C.hashCode());
System.out.println(set.size());
}
测试结果:
结论:
在 Java 中,
equals()
和hashCode()
是用于对象相等性和哈希表存储的两个重要方法。它们在一起工作,因为它们在不同的上下文中被使用。当重写
equals()
方法时,目的是定义两个对象在逻辑上是否相等。默认情况下,equals()
方法是从Object
类继承的,它比较的是对象的引用是否相等,即是否是同一个对象。但在很多情况下,我们希望自定义相等的逻辑,例如,根据对象的某些属性来判断两个对象是否相等。然而,在使用具有哈希表的数据结构(如
HashMap
、HashSet
等)时,哈希表使用对象的hashCode()
方法来确定对象在表中的存储位置。哈希表通过哈希码来快速定位对象,提高数据访问的效率。如果你在重写
equals()
方法时不重写hashCode()
方法,可能会导致以下问题:
不一致的行为:如果两个对象根据
equals()
方法被判断为相等,但它们的hashCode()
返回不同的值,那么在使用哈希表存储这些对象时,它们将被放在不同的桶中。这样在查找时就无法正确找到对应的对象。违反哈希表约定:根据 Java 规范,如果两个对象相等(即
equals()
方法返回true
),那么它们的hashCode()
方法应该返回相同的哈希码。否则,它们将无法正确地处理哈希表的数据结构。因此,为了保持一致性并遵守哈希表的约定,任何时候重写
equals()
方法时都应该同时重写hashCode()
方法,以确保相等的对象具有相同的哈希码,并且能够正确地在哈希表中进行存储和查找。在实际编程中,这两个方法通常是一起重写的。