当前位置: 首页 > 后端技术 > Java

为什么重写equals就一定要重写hashCode

时间:2023-04-01 19:44:15 Java

equals面试常见问题开始说之前,先看几个面试常见问题,看看你能不能全部答对。1、等于和==有什么区别?2.hashcode相等的两个对象一定==相等吗?等于等于吗?3.如果两个对象使用equals相等,那么它们的哈希码是否相等?如果我们不覆盖equals和hashcode,那么它使用Object方法的实现。让我们简单看一下publicbooleanequals(Objectobj){return(this==obj);}publicstaticinthashCode(Objecto){returno!=null?o.hashCode():0;}为什么要重写从上面的代码可以看出,Object提供的equals不是比较值,而是比较内存地址。由此可知,要使用equals来比较对象,那么equals必须要重写。重写equals不重写hashCode会存在什么问题?我们先来看下面这段话。在每个覆盖equals方法的类中,必须覆盖hashCode。如果你不这样做,你就违反了hashCode的一般契约,这就是上面评论中所说的。因此,该类无法组合,只能与哈希集合正常工作,哈希集合指的是HashMap、HashSet、HashTable、ConcurrentHashMap。来自EffectiveJava第3版结论:如果您在不覆盖hashCode的情况下覆盖equals,它将无法与散列集合一起正常工作。既然如此,就拿我们最熟悉的HashMap来进行论证和推导吧。我们知道HashMap中的key是不能重复的。如果重复添加,后面添加的会覆盖前面的内容。那我们看看HashMap是如何判断key的唯一性的。staticfinalinthash(Objectkey){inth;返回(键==空)?0:(h=key.hashCode())^(h>>>16);}查看代码,发现是通过计算Mapkey的hashCode值来确定链表中的存储位置。那么可以推断,如果重写equals而不重写hashCode,那么可能会出现重复元素的矛盾。下面我们来表演一下publicclassEmployee{privateStringname;privateIntegerage;publicEmployee(Stringname,Integerage){this.name=name;this.age=age;}@Overridepublicbooleanequals(Objecto){if(this==o)returntrue;如果(o==null||getClass()!=o.getClass())返回false;雇员employee=(Employee)o;returnObjects.equals(name,employee.name)&&Objects.equals(age,employee.age);}/*@OverridepublicinthashCode(){returnObjects.hash(name,age);}*/}publicstaticvoidmain(String[]args){Employeeemployee1=newEmployee("冰峰",20);Employeeemployee2=newEmployee("冰峰",22);Employeeemployee3=newEmployee("冰峰",20);HashMapmap=newHashMap<>();map.put(employee1,"1");map.put(employee2,"1");map.put(employee3,"1");System.out.println("等于:"+employee1.equals(employee3));System.out.println("hashCode:"+(employee1.hashCode()==employee3.hashCode()));System.out.println(JSONObject.toJSONString(map));}按照正常情况,map中只有两个元素。这个问题出现在employee2和employee3的执行结果中是因为没有重写hashCode,导致map在计算key的hash值时,除了hash值不一致外,计算的都是绝对值相同的对象。接下来我们打开hashCode的注释代码,可以看到执行结果汇总。如果重写equals,则必须重写hashCode。如果不重写,会造成与哈希集合(HashMap、HashSet、HashTable、ConcurrentHashMap)的冲突。