美文网首页程序员代码改变世界Android知识
为什么覆盖了equals就必须覆盖hashCode?

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

作者: 简xiaoyao | 来源:发表于2017-10-09 22:06 被阅读128次

    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<PhoneNumber, String> 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?

        本文链接:https://www.haomeiwen.com/subject/ffucyxtx.html