为什么覆盖了equals就必须覆盖hashCode?


为什么覆盖了equals就必须覆盖hashCode?_第1张图片
two.jpg

很多人可能都知道,在每个覆盖了equals方法的类中,也必须覆盖hashCode方法;但是这里面的原因是什么呢?

我想应该从两个方面阐述这个问题:

  • 什么情况下需要覆盖equals
  • 在覆盖了equals的同时未覆盖hashCode会导致什么问题?

第一个问题,什么情况下需要覆盖equals呢?首先我们需要知道,如果不覆盖equals,由于每个类的实例在内存中都是唯一的,那么就无法做到两个业务上等同的实例equals也返回true,所以说覆盖equals一般都是业务需要,即有些场景下只需要对象的关键属性相等,就认为他们相等

其次,如果在覆盖了equals的同时未覆盖hashCode会导致什么问题呢?这个问题的答案在于:如果没有遵守覆盖equals时同时覆盖hashCode,就会违反Object.hashCode的通用约定(具体的约定大家可以至ObejcthashCode方法注释处查看),从而导致该类无法结合所有基于散列的集合一起正常工作,这样的集合包括HashMapHashSetHashTable,有点难理解是吗?没关系,举例说明就清楚了,假设有一个PhoneNumber类,只需两个实例的prefixlineNumber属性相等,即可认为两个PhoneNumber相等:

public class PhoneNumber {
    private short areaCode;
    private short prefix;//关键属性
    private short lineNumber;//关键属性

    public PhoneNumber(short areaCode, short prefix, short lineNumber) {
        this.areaCode = areaCode;
        this.prefix = prefix;
        this.lineNumber = lineNumber;
    }

    public boolean equals(Object o) {
        if(o == this) {
            return true;
        }

        if(!(o instanceof PhoneNumber)) {
            return false;
        }

        PhoneNumber compareO = (PhoneNumber)o;

        return compareO.prefix == this.prefix
                && compareO.lineNumber == this.lineNumber;
    }
}

然后将PhoneNumberHashMap一起使用:

Map m = new HashMap<>();
m.put(new PhoneNumber(21, 37, 3245), "xiaobai");

System.out.println(m.get(new PhoneNumber(53, 37, 3245)));//输出:null

你可能期望输出的是:"xiaobai",因为putget的两个key对象的equals返回是true,但实际上得到的却是null,为什么呢?原因就在于未覆盖hashCode,两个对象即便相等,但是其hashCode还是可能不等,那么put方法把PhoneNumber对象存放至一个散列桶(hash bucket)中,而get方法却在另外一个散列桶中查找这个对象,当然找不到,退一步说,即便是两个实例刚好被放至于同一个散列桶中,get方法依然还是会返回null,因为HashMap有一项机制:如果两个实例散列码不匹配, 直接放弃比较其等同性

我想,讲到这里大家应该清楚了为什么覆盖了equals方法的同时必须覆盖hashCode的原因所在,但有人可能会说,我这个对象不会和基于散列的集合一起使用的,所以不需要遵守这个约定,但正所谓魔鬼隐藏在细节之中,你现在不会使用不代表以后不会使用(不要相信自己的记忆);你知道不能一起使用,不代表别人知道(即便有注释别人也不一定会看)。所以建议一般情况下都遵守这项约定,只是稍微增加一点工作量而已,却是代码健壮的基石

你可能感兴趣的:(为什么覆盖了equals就必须覆盖hashCode?)