为什么重写equals()就要重写hashcode()

1.什么是equals()和hashcode()

    hashcode()方法是Object类的一个本地方法 一个对象调用hashcode方法返回的是将该对象在堆中的地址通过哈希算法计算出的哈希值,每次调用返回的都是同一个哈希值
public native int hashCode();
    equals()方法也是Object类的方法 通过 == 比较两个对象 在Java中 如果通过==比较两个引用类型则比较他们所指向的对象在堆中是否为同一块地址
public boolean equals(Object obj) {
          
   
        return (this == obj);
}
    既然equals方法可以通过地址比较是否为同一对象,那么为什么还需要hashcode()方法?因为用equals()方法比较效率比较低,但利用hashcode()进行比较,只需比较生成的hash值就可以了,但要注意的是不同对象可能有同一个hash值,所以我们往往将equals()方法和hashcode()搭配使用,既确保了效率,也保证了比较的正确性

2.为什么重写equals()就要重写hashcode()

    原因1 在Java doc官方文档中对equals方法的说明 Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes. 对hashcode()的说明 If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

所以在Java开发者规定语义的角度上来说,只要两个对象equals比较返回true,那么这两个对象的hash值一定要相等

    原因2 为了使用散列表

首先我们要知道为什么要重写equals和hashcode 从业务逻辑的角度上来看,有时候我们认为两个对象相等,是认为他们的所有属性都对应相等,而不是两个对象所对应的内存地址相等 假如有一个Dog类

public class Dog {
          
   

    private String name;
    private String type;
    private Integer age;

}

有两个对象,我们认为这两个对象是相等的

Dog dog1 = new Dog("jbc", "big", 18);
Dog dog2 = new Dog("jbc", "big", 18);

所以需要重写这两个方法,要求只要他们的属性都完全相等,那么调用这两个方法比较结果就是相等的

@Override
    public boolean equals(Object o) {
          
   
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Dog dog = (Dog) o;
        return Objects.equals(name, dog.name) && Objects.equals(type, dog.type) && Objects.equals(age, dog.age);
    }

    @Override
    public int hashCode() {
          
   
        return Objects.hash(name, type, age);
    }

说明了为什么要重写这两个方法,现在我们就要解释为什么重写equals就必须重写hashcode

Java中的集合例如HashMap, HashTable 要求key的唯一性,假如我们存放两个key为Dog的对象 Dog dog1 = new Dog(“jbc”, “big”, 18); Dog dog2 = new Dog(“jbc”, “big”, 18);

根据HashMap的底层源码 如果只重写equals方法不重写hashcode方法 那么他们的hash值一定不相等,就会造成一个map中存在两个相同Dog对象 在不同的索引位置 如果只重写hashcode()不重写equals()就会在equals比较中返回false,也会造成两个相同的Dog在一个索引的同一条链上

if (p.hash == hash &&
       ((k = p.key) == key || (key != null && key.equals(k))))

总结:

对于为什么重写equals()就要重写hashcode()这个问题,需要从几个方面来解释

  1. 认为两个对象相等的逻辑是他们的属性都对应相等,而不是地址相等
  2. 使用散列表时为了保证在 1 的逻辑下 不存在重复对象
  3. 从语义的角度 如果equals比较相等时,保证hash值也要相等
经验分享 程序员 微信小程序 职场和发展