为何重写了equals()也要重写hashCode()

为何重写了equals()也要重写hashCode()

笔者文笔功力尚浅,若有不妥,请慷慨指出,一定感激涕零java

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

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

equals方法

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

1public boolean equals(Object obj) {
2    return (this == obj);
3}
复制代码

咱们发如今equals()方法中就关键的==,那么==在Java中有什么含义呢,咱们都知道在Java中分为基本数据类型和引用数据类型。那么==在这两个类型中做用是不同的。面试

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

基本数据类型包括:byte,short,char,int,long,float,double,boolean算法

而经过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类型的哈希值。数组

1public native int hashCode();
复制代码

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

  • 若是对象在使用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,而且自定义的类不重写equalshashCode方法会发生什么。

自定义的类

1@Builder
2@NoArgsConstructor
3@AllArgsConstructor
4class CustomizedKey{
5    private Integer id;
6    private String name;
7}
复制代码

接下来咱们看使用自定义的类做为key

 1    public static void main(String[] args{
2
3        Map<CustomizedKey, Integer> data = getData();
4
5        CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
6
7        Integer integer = data.get(key);
8
9        System.out.printf(String.valueOf(integer));
10    }
11
12    private static Map<CustomizedKey,Integer> getData(){
13        Map<CustomizedKey,Integer> customizedKeyIntegerMap = new HashMap<>();
14        CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
15        customizedKeyIntegerMap.put(key,10);
16        return customizedKeyIntegerMap;
17    }
复制代码

咱们能够看到程序最后打印的是一个null值。缘由正如上面咱们说的同样。

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

接下来咱们就重写一下这两个方法。若是咱们使用IDEA的话,那么直接使用快捷键便可。

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

 1@Builder
2@NoArgsConstructor
3@AllArgsConstructor
4class CustomizedKey{
5    private Integer id;
6    private String name;
7
8    @Override
9    public boolean equals(Object o) {
10        if (this == o) return true;
11        if (o == null || getClass() != o.getClass()) return false;
12        CustomizedKey that = (CustomizedKey) o;
13        return Objects.equals(id, that.id) &&
14                Objects.equals(name, that.name);
15    }
16
17    @Override
18    public int hashCode() {
19        return Objects.hash(id, name);
20    }
21}
复制代码

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

咱们也可以使用Lombok提供的@EqualsAndHashCode注解简化代码

本文代码地址

往期文章

参考文章

相关文章
相关标签/搜索