为什么要同时重写hashcode和equals?

首先看看jdk中objec的hashCode方法的定义:
1.相同的对象,返回的hash code一定是一样的
2.如果两个对象调了equals返回true,则两个对象的hash code是一样的
3.如果两个对象调了equals返回false,则两个对象的hashcode不一样,这种特性会提高hash table的效率
再看equals方法的定义:
假如equals被重写,则hashcode也需要被重写,来维护一个特性:当两个对象调equals返回true,则两个对象的hashcode是一致的

那总结一下,其实在jdk的文档中,要求,当equals方法被重写,hashcode也必须要被重写。
目的:要维持对象的一个规则,当两个对象调equals返回true,则两个对象的hashcode是一致的。

但不重写,编译器也不会告知错误。

然后为什么要维持这个特性?因为jdk基于这个特性,实现了HashMap或HashSet等类,key在使用之前都经过hashcode,所以,假如一个类只重写equals方法,创建了两个字段值一致的对象(equals返回true),而hashcode没有重写,默认根据内存地址来计算,所以两者不一致。
而HashMap或者HashSet里面判断元素相等会用hash值来做判断,这就和equals方法返回的结果不一致,也就造成了逻辑上的错误。

拿源码说话:HashMap的get方法定义的注释说明如下:

从使用逻辑上来讲:key只要不为null,equals方法返回true,那就能得到相同的value。

实际上呢,get方法的底层实现如下:

可以看到,实现上其实是同时判断了 “hashCode是否一致” 和 “是否equals” ,因为它遵循了JDK对hashCode的约定(两个对象调了equals返回true,则两个对象的hash code是一样的)。

所以并不是说,重写equals不重写hashCode一定会出问题,而是在使用一些 “遵循了jdk约定 规范的类及方法” 上(就比如用了HashMap),会有逻辑理解上的偏差,导致业务处理逻辑出现问题。所以应该尽量遵循jdk约定,避免这种偏差带来业务的错误处理结果。

最后再说一句,那我只实现hashcode行不?其实也可以~(至少我目前看来是这样,假如认知错误,欢迎指正)
最最后再说一句,还记得上面说的“如果两个对象调了equals返回false,则两个对象的hashcode不一样,这种特性会提高hash table的效率”吗?
可以看下HashTable的源码:

public synchronized boolean containsKey(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return true;
        }
    }
    return false;
}


可以看到判断有没有包含key,是会同时判断hash值和equals的返回结果的,假如两个不equal的对象,返回的hashcode不相等,其实if ((e.hash == hash) && e.key.equals(key)只会执行第一段而已,效率就提高了

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
如果你需要返回`List<User>`类型的不同元素列表,你可以稍作修改代码。以下是修改后的示例: ```java import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; class User { private int id; private String name; public User(int id, String name) { this.id = id; this.name = name; } // getter和setter方法 // 重写equals()和hashCode()方法 @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof User)) { return false; } User other = (User) obj; return this.id == other.id && this.name.equals(other.name); } @Override public int hashCode() { return Objects.hash(id, name); } } public class Main { public static void main(String[] args) { // 创建两个列表 List<User> list1 = new ArrayList<>(); list1.add(new User(1, "Alice")); list1.add(new User(2, "Bob")); list1.add(new User(3, "Charlie")); List<User> list2 = new ArrayList<>(); list2.add(new User(2, "Bob")); list2.add(new User(3, "Charlie")); list2.add(new User(4, "David")); // 使用Stream API筛选不同的元素 List<User> differentElements = list1.stream() .filter(e -> !list2.contains(e)) .collect(Collectors.toList()); // 打印结果 System.out.println("不同的元素:" + differentElements); } } ``` 在这个示例中,我们修改了`User`类,添加了构造函数、getter和setter方法。同时,重写了`equals()`和`hashCode()`方法来确保正确地比较`User`对象。 然后,我们使用Stream API的`filter`方法来筛选出在`list1`中存在但是在`list2`中不存在的用户,并将结果收集到一个新的`List<User>`中。 输出结果将是不同的用户列表,根据你的具体数据而定。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值