为什么覆盖equals时也要覆盖hashCode方法

看了《effective java》第九条。特此对equals方法和hashCode方法做此总结

现在假设我们有一个矩形类Rectangle 

public class Rectangle {
    private int length;
    private int width;

    public Rectangle(int length, int width) {
        this.length = length;
        this.width = width;
    }
}

先回顾下我们为什么要覆盖equals方法呢?现在我们有两个矩形类的对象实例rectangle1 和rectangle2。

        Rectangle rectangle1 = new Rectangle(3, 4);
        Rectangle rectangle2 = new Rectangle(4, 3);

我想知道两个矩形是否是相等的,如果使用==运算符的话,比较的是rectangle1的引用和rectangle2的引用分别指向的对象在堆中存储的位置。两个对象是先后new出来的,所以肯定是在堆中有两个不同的对象,得到的结果就必然是false。

        System.out.println(rectangle1 == rectangle2);
输出结果:false

这并不是我们想要的,我们的目的并不是想知道这两个引用是否是指向同一个对象,而是想知道这两个矩形的长宽是否相同,简单来说我们想要的是通过逻辑判断来得知这两个矩形rectangle1和rectangle2是否相等。所以我们通过覆盖equals方法来实现。

    @Override
    public boolean equals(Object obj) {
        Rectangle o = (Rectangle) obj;
        if (length == o.length){
            if (width == o.width){
                return true;
            }else return false;
        } else if (width == o.length) {
            if (length == o.width) {
                return true;
            }else return false;
        }
        return false;
    }

这个equals方法中,我们制定的规则是两个矩形,有相同的长、宽,即认为两个矩形是相同的。

这时再通过equals方法来判断两个矩形是否相等

        System.out.println(rectangle1.equals(rectangle2));

输出结果: true

到这里我们已经满足了我们判断两个矩形是否相同的需求了。

那我们为什么还要覆盖hashCode方法呢?

如果这个矩形类是需要放进类似HashMap之类的基于哈希表来实现的容器中并作为key时,就有必要覆盖hashCode方法,按照我们自定的规则来得到一个矩形对象的hash值。(如果不指定,默认一般是通过将该对象的内部地址转换成一个整数来实现的

        HashMap<Rectangle, String> map2 = new HashMap<>(16);
        map2.put(rectangle1, "nice");
        map2.put(rectangle2, "bad");
        System.out.println("size:"+map2.size());

输出结果:size:2

我们已经认为rectangle1和rectangle2在逻辑上是相同的了,也就是rectangle1和rectangle2是同一个key了,在put方法添加rectangle2的时候,“bad”理应覆盖掉“nice”,size应该为1,但事实却并发我们想的这样,哈希表在计算数组下标的时候,是会调用此对象的hashCode方法,如果我们没有覆盖hashCode方法,计算hash值时则会将该对象的堆地址转换成一个整数hash值,这在很大概率上是会将rectangle1和rectangle2分配在两个不同的散列桶中。

想要让rectangle1和rectangle2做为同一个key,在散列时落在同一个桶中,我们就必须要来覆盖hashCode方法

    @Override
    public int hashCode() {
        //以下的17、31都是任意指定大小的数字。
        int result = 17;
        //这里我简单指定面积相同的矩形,hashCode值就相同
        result = 31 * result + length*width;
        return result;
    }

这时再试一次

        HashMap<Rectangle, String> map2 = new HashMap<>(16);
        map2.put(rectangle1, "nice");
        map2.put(rectangle2, "nice");
        System.out.println("size:"+map2.size());

输出结果:size:1

这下得到了我们想要的结果。

我们在覆盖了hashCode方法之后,要问问自己”我们认为逻辑相等的实例是否都具有相等的散列码”。通过编写单元测试来验证一下你的推断。

总结:需要做逻辑相等判断的类,覆盖equals方法,如果还需要在散列表(HashMap、HashSet)中作为key,需要覆盖hashcode方法。


文章有些粗糙,如果错误,欢迎指出~~


  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值