前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >为什么重写了equals()也要重写hashCode()

为什么重写了equals()也要重写hashCode()

作者头像
公众号 IT老哥
发布2020-12-29 15:31:47
1K0
发布2020-12-29 15:31:47
举报

前言

在Effective Java中第九条规定在覆盖equals()方法时总要覆盖hashCode()方法。这是为什么呢?接下来我们就介绍一下这两个方法。

Java中的equals()方法和hashCode()方法都是在Object类中的方法,而在Java中所有的类都是Obejct类的子类,所以Java中所有的方法都会有这两个方法的默认实现。

equals方法

Object类中的equals()方法定义如下

代码语言:javascript
复制
public boolean equals(Object obj) {
    return (this == obj);
}

我们发现在equals()方法中就关键的==,那么==在Java中有什么含义呢。

我们都知道在Java中分为基本数据类型和引用数据类型。那么==在这两个类型中作用是不一样的。

  • 基本数据类型:比较的是==两边值是否相等
  • 引用数据类型:比较的是==两边内存地址是否相等

基本数据类型包括:byteshortcharintlongfloatdoubleboolean

而通过Java文档中的equals()方法描述,所有要实现自己的equals()方法都要遵守下面几个规则

  • 自反性:对于任何对象x,x.equals(x)应该返回true
  • 对称性:对于任何两个对象x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true
  • 传递性:对于多个对象x、y、z,如果x.equals(y)返回true,y.equals(z)返回true,那么y.equals(z)也应该返回true
  • 一致性:对于两个非空对象x、y,在没有修改此对象的前提下,多次调用返回的结果应该相同
  • 对于任何非空的对象x,x.equals(null)都应该返回false

hashCode方法

Object中的hashCode()方法是一个本地方法,返回一个int类型的哈希值。

代码语言:javascript
复制
public native int hashCode();

在hashCode()方法中也有一些规约

  • 如果对象在使用equals方法中进行比较的参数没有修改,那么多次调用一个对象的hashCode()方法返回的哈希值应该是相同的。
  • 如果两个对象通过equals方法比较是相等的,那么要求这两个对象的hashCode方法返回的值也应该是相等的。
  • 如果两个对象通过equals方法比较是不同的,那么也不要求这两个对象的hashCode方法返回的值是相同的。但是我们应该知道对于不同对象产生不同的哈希值对于哈希表(HashMap等等)能够提高性能。

equals方法和hashCode方法会在哪用到

这两个方法经常出现在Java中的哪个类里面呢?如果看过HashMap源码的应该了解这两个方法经常出现在HashMap中。

网上介绍HashMap类的文章有很多了,这里就简单介绍一下HashMap。

当一个节点中的链表超过了8的时候就会变为红黑树,以解决链表长度过长以后查询速度慢的缺点。

HashMap是由数组和链表组成的高效存储数据的结构。那么是如何确定一个数据存储在数组中的哪个位置呢?

就是通过hashCode方法进行计算出存储在哪个位置,还记得我们上面讲hashCode方法说了有可能两个不同对象的hashCode方法返回的值相同。

那么此时就会产生冲突,产生冲突的话就会调用equals方法进行比对,如果不同,那么就将其加入链表尾部,如果相同就替换原数据。

计算位置当然不是上面简单的一个hashCode方法就计算出来,中间还有一些其他的步骤,这里可以简单的认为是hashCode确定了位置。

什么时候去覆盖这两个方法呢?

如果你不将自定义的类定义为HashMap的key值的话,那么我们重写了equals方法而没有重写hashCode方法,编译器不会报任何错,在运行时也不会抛任何异常。

如果你想将自定义的类定义为HashMap的key值得话,那么如果重写了equals方法那么就必须也重写hashCode方法。

接下来我们可以看一下我们使用自定义的类作为HashMap的key,并且自定义的类不重写equals和hashCode方法会发生什么。

代码语言:javascript
复制
@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomizedKey{
    private Integer id;
    private String name;
}
代码语言:javascript
复制
public static void main(String[] args) {

    Map<CustomizedKey, Integer> data = getData();

    CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();

    Integer integer = data.get(key);

    System.out.printf(String.valueOf(integer));
}

private static Map<CustomizedKey,Integer> getData(){
    Map<CustomizedKey,Integer> customizedKeyIntegerMap = new HashMap<>();
    CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
    customizedKeyIntegerMap.put(key,10);
    return customizedKeyIntegerMap;
}

我们可以看到程序最后打印的是一个null值。原因正如上面我们说的一样。

hashCode:用来计算该对象放入数组中的哪个位置,因为是两个都是new的对象,所以即使里面的值一样,但是对象所处的地址却不同,所以使用默认的hashCode也就不同,当然在hashMap中就不会认为两个是一个对象。

接下来我们就重写一下这两个方法。如果我们使用IDEA的话,那么直接使用快捷键即可。

接下来我们看我们实现的两个方法

代码语言:javascript
复制
@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomizedKey{
    private Integer id;
    private String name;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CustomizedKey that = (CustomizedKey) o;
        return Objects.equals(id, that.id) &&
                Objects.equals(name, that.name);
    }

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

然后我们再次运行上面的程序发现输出打印已经变成了10。

源于:jianshu.com/p/53e233e8abad

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT老哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • equals方法
  • hashCode方法
  • equals方法和hashCode方法会在哪用到
  • 什么时候去覆盖这两个方法呢?
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档