为什么重写equals必须重写hashCode
1. Object中equals、hashCode
public native int hashCode();public boolean equals(Object obj) { return (this == obj);}
首先看一下equals
办法,Object中默认比拟两个对象的地址。也就是说如果一个类默认的equals
,就比拟地址。而反观hashCode
办法,咱们先来看一下hashCode
正文
/** * Returns a hash code value for the object. //返回的是一个对象的hash值 * This method is * supported for the benefit of hash tables such as those provided by * {@link java.util.HashMap}. * <p> * The general contract of {@code hashCode} is: * <ul> * <li>Whenever it is invoked on the same object more than once during * an execution of a Java application, the {@code hashCode} method * must consistently return the same integer, //这里又是要害,无论调用多少次hashCode,同一个对象都会返回同一个hash值 * provided no information * used in {@code equals} comparisons on the object is modified. // 这里没看懂 然而测试了一下,如果同一个对象属性变动了,它的hash值也会不变 * This integer need not remain consistent from one execution of an * application to another execution of the same application. * <li>If two objects are equal according to the {@code equals(Object)} * method, then calling the {@code hashCode} method on each of * the two objects must produce the same integer result. //如果equals比拟两个对象相等,那么这两个对象必须就有雷同的hash值 * <li>It is <em>not</em> required that if two objects are unequal * according to the {@link java.lang.Object#equals(java.lang.Object)} * method, then calling the {@code hashCode} method on each of the * two objects must produce distinct integer results. However, the * programmer should be aware that producing distinct integer results * for unequal objects may improve the performance of hash tables. * </ul> * <p> * As much as is reasonably practical, the hashCode method defined by * class {@code Object} does return distinct integers for distinct * objects. (This is typically implemented by converting the internal * address of the object into an integer, but this implementation * technique is not required by the * Java™ programming language.) * * @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */
再联合Effective Java 第三版
中所提到的
每个笼罩了equals办法的类中,必须笼罩hashCode。如果不这么做,就违反了hashCode的通用约定,也就是下面正文中所说的。进而导致该类无奈联合所以与散列的汇合一起失常运作,这里指的是HashMap、HashSet、HashTable、ConcurrentHashMap。
2. equals和hashCode同时存在的意义
equals
和hashCode
都是用来判断两个对象想不想等的,那么问题来了?
为什么须要两个呢?
- equals - 保障比拟对象是否是相对相等的
- hashCode - 保障在最快的工夫内判断两个对象是否相等,可能有误差值
一个是保障牢靠,一个是保障性能。也就是说:
- 同一个对象的hashCode肯定相等,不同对象的hashCode也可能相等,这是因为hashCode是依据地址hash进去的一个int 32 位的整型数字,相等是在劫难逃。
- equals比拟的是两个对象的地址,同一个对象地址必定雷同,不同的对象地址肯定不同,可靠性是这么来的。
就像HashMap外面插入时判断:
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
判断两个key是否雷同逻辑
- 先比拟 hash (通过hashCode的高16位和低16位进行异或运算得出的) ,因为两个雷同的对象hash值肯定相等。
- 再比拟两个对象的地址是否雷同,== 判断是否相对相等,而equals判断是否主观相等。
上面剖析一下简略的不重写hashCode的结果和在存到HashMap中可能呈现的结果
3. 如果只重写equals的结果
- 无论是
Effective Java
,还是阿里巴巴Java标准手册
都是要求重写equals,必须重写hashCode。 - 两个相等的对象必须具备相等的散列码(Java要害约定)
那么不重写的结果是什么呢?
举一个例子:如果一个只重写了equals(比拟所有属性是否相等)的类 new 出了两个属性雷同的对象。这时能够失去的信息是这个属性雷同的对象地址必定不同,然而equals是true,hashCode返回的是不相等的(个别不会呈现hash碰撞)。
也就是说这个类对象违反了Java对于两个对象相等的约定。违反约定的起因是 牢靠的equals判断两个对象是相等的,然而他们两个的散列码确是不相等的。
总结来说:
- equals 为 true , hashCode 必须相等
- hashCode 相等时 , equals 能够不必为 true (也就是hash碰撞的时候)
4 . 不重写HashCode对于散列表的影响
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
雷同的场景,雷同配方:
两个所有属性都相等的对象,然而地址不同。没重写hashCode
时,p.hash == hash
肯定不相等。然而逻辑上这两个对象是相等的,并且equals
也是相等的。这就会导致,HashMap外面原本有这个key,然而你通知我没有,导致了put操作胜利。逻辑上是不符合规范的,get时取出来的也可能是本人另一个的value。