为什么重写equels方法一定要重写hashCode方法

为什么重写equels方法一定要重写hashCode方法

1、源码

其实上面的Person类我可以只重写equels方法而不写hashCode方法,一样能达到上面的效果。但为什么还是建议写上呢?官方的说法是:对象的equals方法被重写,那么对象的hashCode()也尽量重写

重写equals()方法就必须重写hashCode()方法的原因,从源头Object类讲起就更好理解了。

先来看Object关于hashCode()和equals()的源码:

  public native int hashCode();
        
  public boolean equals(Object obj) {
           return (this == obj);
      }

光从代码中我们可以知道,hashCode()方法是一个本地native方法,返回的是对象引用中存储的对象的内存地址。而equals方法是利用==来比较的也是对象的内存地址。从上边我们可以看出,hashCode方法和equals方法是一致的。还有最关键的一点,我们来看Object类中关于hashCode()方法的注释:

  1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。    
  2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。    
  3.如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。
     但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。      

整理 : hashCode()和equals()保持一致,如果equals方法返回true,那么两个对象的hasCode()返回值必须一样。如果equals方法返回false,hashcode可以不一样,但是这样不利于哈希表的性能,一般我们也不要这样做。

假设两个对象,重写了其equals方法,其相等条件是某属性相等,就返回true。如果不重写hashcode方法,其返回的依然是两个对象的内存地址值,必然不相等。这就出现了equals方法相等,但是hashcode不相等的情况。这不符合hashcode的规则。

2、HashSet和Map集合类型

重写equals()方法就必须重写hashCode()方法主要是针对HashSet和Map集合类型,而对于List集合倒没什么影响。

原因: 在向HashSet集合中存入一个元素时,HashSet会调用该对象(存入对象)的hashCode()方法来得到该对象的hashCode()值,然后根据该hashCode值决定该对象在HashSet中存储的位置。简单的说:HashSet集合判断两个元素相等的标准是:两个对象通过equals()方法比较相等,并且两个对象的HashCode()方法返回值也相等。如果两个元素通过equals()方法比较返回true,但是它们的hashCode()方法返回值不同,HashSet会把它们存储在不同的位置,依然可以添加成功。

这就是问题所在:就是如果你只重写equals()方法,而不重写hashCode(),如果equals()为true,而它们的hashCode()方法返回值肯定不一样,因为它们都不是同一对象所以内存地址肯定不一样,所以它还是添加成功了,那么其实你写的equals()方法根本没啥软用。

3、代码示例

1、People类

重写equals方法,但并没有hashCode方法。

public class People {
    private String name;
    private Integer age;

    public People(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 重写 equals 方法
     */
    @Override
    public boolean equals(Object obj) {
        People u = (People) obj;
        return this.getName().equals(u.getName()) && (this.age.equals(u.getAge()));
    }
    /**
     * 重写 toString 方法
     */
    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2、实现类

    public static void main(String[] args) {
        HashSet<People> hashSet = Sets.newHashSet();
        People people1 = new People("小小",3);
        People people2 = new People("中中",4);
        People people3 = new People("中中",4);
        People people4 = new People("大大",5);
        hashSet.add(people1);
        hashSet.add(people2);
        hashSet.add(people3);
        hashSet.add(people4);

        System.out.println(hashSet);
        //输出:[People{name='小小', age=3}, People{name='中中', age=4}, People{name='大大', age=5}, People{name='中中', age=4}]
    }

很明显,我重写了equals方法,那么people2和people3的equals应该相同,所以不能放入HashSet,但它们的hashCode()方法返回不同,所以导致同样能放入HashSet。

重点:对于Set集合必须要同时重写这两个方法,要不然Set的特性就被破坏了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨鸦_Cormorant

大家喜欢的话可以点个关注投币哟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值