为什么说任何时候重写equals,都必须同时重写hashCode

关于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 字符串的哈希码相加。这样的实现可能会导致哈希码的分布不均匀,从而降低哈希表的性能。特别是当 idname 的范围很大时,相加后的结果可能会导致哈希码冲突较多,从而导致哈希表的查找效率下降。

这两种方式都可以用,现在测试:

测试代码:

    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 类继承的,它比较的是对象的引用是否相等,即是否是同一个对象。但在很多情况下,我们希望自定义相等的逻辑,例如,根据对象的某些属性来判断两个对象是否相等。

        然而,在使用具有哈希表的数据结构(如 HashMapHashSet 等)时,哈希表使用对象的 hashCode() 方法来确定对象在表中的存储位置。哈希表通过哈希码来快速定位对象,提高数据访问的效率。

如果你在重写 equals() 方法时不重写 hashCode() 方法,可能会导致以下问题:

  1. 不一致的行为:如果两个对象根据 equals() 方法被判断为相等,但它们的 hashCode() 返回不同的值,那么在使用哈希表存储这些对象时,它们将被放在不同的桶中。这样在查找时就无法正确找到对应的对象。

  2. 违反哈希表约定:根据 Java 规范,如果两个对象相等(即 equals() 方法返回 true),那么它们的 hashCode() 方法应该返回相同的哈希码。否则,它们将无法正确地处理哈希表的数据结构。

因此,为了保持一致性并遵守哈希表的约定,任何时候重写 equals() 方法时都应该同时重写 hashCode() 方法,以确保相等的对象具有相同的哈希码,并且能够正确地在哈希表中进行存储和查找。在实际编程中,这两个方法通常是一起重写的。

猜你喜欢

转载自blog.csdn.net/weixin_49561506/article/details/131873431