为什么在重写equals方法的时候需要重写hashCode方法
首先
Object.hashCode的通用约定《Effective Java》有一条是():
如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果。
如果只重写了equals方法而没有重写hashCode方法的话,则会违反约定,因为相等的对象必须具有相等的散列码(hashCode)。
其次
其次要明白为什么会有这么个约定,这就要来看下HashMap和HashSet这些基于散列值(hash)实现的类了。
先了解下HashMap的工作原理:
HashMap的主干是一个Entry数组,每一个Entry包含一个key-value键值对。其中的关键是数组下标的处理,数组的下标是根据传入元素的hashCode方法的返回值再和特定的值异或得到的。
得到下标i后,如果table[i]==null,直接新建节点添加,否则,判断table[i]的首个元素是否和传入元素的key一样,如果相同直接覆盖value,这里的相同指的是equals。
如果不相同,判断table[i]是否是红黑树,如果是红黑树,则直接在树中插入键值对。遍历过程中若发现key已经存在直接覆盖value即可。
如果不是红黑树,判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作。同样,遍历过程中若发现key已经存在直接覆盖value即可。
我们举个小例子来看看,如果重写了equals而不重写hashcode会发生什么样的问题
public class MyTest {
private static class Person{
int idCard;
String name;
public Person(int idCard, String name) {
this.idCard = idCard;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()){
return false;
}
Person person = (Person) o;
//两个对象是否等值,通过idCard来确定
return this.idCard == person.idCard;
}
}
public static void main(String []args){
HashMap<Person,String> map = new HashMap<Person, String>();
Person person = new Person(1234,"乔峰");
//put到hashmap中去
map.put(person,"天龙八部");
//get取出,从逻辑上讲应该能输出“天龙八部”
System.out.println("结果:"+map.get(new Person(1234,"萧峰")));
}
}
实际输出结果:null
如果我们已经对HashMap的原理有了一定了解,这个结果就不难理解了。尽管我们在进行get和put操作的时候,使用的key从逻辑上讲是等值的(通过equals比较是相等的),但由于没有重写hashCode方法,所以put操作时,key(hashcode1
)–>hash–>indexFor–>最终索引位置 ,而通过key取出value的时候 key(hashcode2
)–>hash–>indexFor–>最终索引位置,由于hashcode1
不等于hashcode2
,导致没有定位到一个数组位置而返回逻辑上错误的值null(也有可能碰巧定位到一个数组位置,但是也会判断其entry的hash值是否相等。)
所以,在重写equals的方法的时候,必须注意重写hashCode方法,同时还要保证通过equals判断相等的两个对象,调用hashCode方法要返回同样的整数值。而如果equals判断不相等的两个对象,其hashCode可以相同(只不过会发生哈希冲突,应尽量避免)。
通过这个例子,说明了如果不重写hashCode的话,可能导致HashSet、HashMap不能正常的运作。
来自:
java中为什么要重写hashCode和equals? - FreeOC - 博客园
Java集合之一—HashMap_深入浅出学JAVA-CSDN博客_hashmap
【java】HashMap 一遍就懂!!!!_疯-CSDN博客_hashmap