以下内容包含一定的个人主观论述与观点,有不足之处还请多多包涵,欢迎大家在评论区批评指正!
一、重写 equals() 方法时为什么要重写 hashcode() 方法?
因为 hash 比 equals 方法的开销要小,速度更快,所以在涉及到 hashcode 的容器中(比如 HashSet),判断自己是否持有某个对象时,会先检查 hashCode 是否相等,如果 hashCode 不相等,就会直接认为不相等,并存入容器中,不会再调用 equals 进行比较。这样就会导致,即使该对象已经存在 HashSet 中,但是因为 hashCode 不同,还会再次被存入。
如果只重写了 equals() 方法而不重写 hashcode() 方法,则会在比较时调用父类的 hashcode() 方法,由于是直接依托内存地址进行 hashcode 值的转换,所以在数据量较少时,新建的具有相同内容的两个对象的 hashcode 值一般是不同的,而根据规则经过 equals() 方法判定相同的对象的 hashcode 值必须相同,因此应该重写 hashcode() 方法,在进行比较时会调用同一个自定义的 hashcode() 方法产生相同的 hashcode 值,这样才能保证:如果 equals() 判断是相等的,那 hashCode 值也要相等。
二、具体编码实例
1. 编写一个只重写equals() ,不重写hashCode() 的实体类进行测试
package com.sit.demo;
import java.util.HashSet;
import java.util.Objects;
public class Product {
private Integer id;
private String name;
public Product(Integer id, String name) {
this.id = id;
this.name = name;
}
// 重写 equals() 仅依据 id 是否相等进行判断
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Product product = (Product) o;
return Objects.equals(id, product.id);
}
public static void main(String[] args) {
Product product1 = new Product(1, "水果");
Product product2 = new Product(1, "蔬菜");
HashSet<Product> products = new HashSet<Product>();
products.add(product1);
products.add(product2);
// 使用equals判断是否相等
System.out.println(product1.equals(product2));
// 查看HashSet中元素个数
System.out.println(products.size());
}
}
【测试结果】
true // 可以看到判断是相等的
2 // 两个值都存到了 HashSet中
2. 重写 hashCode() 再次测试
@Override
public int hashCode() {
return Objects.hash(id);
}
【测试结果】
true // 可以看到判断是相等的
1 // 第二个值并没有存到 HashSet中
附:【个人观点】 hash 比 equals 开销小的原因👇
当集合要添加新的元素时,先调用这个元素的 hashcode() 方法,就能很快定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用 equals() 方法的次数就大大降低了。总而言之,在集合查找时,hashcode 能极大地降低对象比较次数,提高查找效率!