java Guide里说到了为什么要重写hashcode的原因:
为什么重写equals时必须重写hashcode方法?
如果两个对象相等,则hashcode一定也是相同的。两个对象相等,对两个对象分别调用equals方法都返回true。但是,两个对象有相同的hashcode值,它们也不一定是相等的。因此,equals方法被覆盖过,则hashcode方法也必须被覆盖。
一定要重写hashcode的主要原因是要保障equals方法的特性,即equals返回结果必须与其hashcode比较结果保持一致。
为什么要这样保障呢?
1、了解hashcode是干啥的
hashcode()的默认行为是对堆上的对象产生独特值。如果没有重写hashcode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
2、了解一个过程
确定和保障对象的唯一性,我们在使用set和map的时候有下面这样一个先hashcode确定唯一性的过程。
当你把对象加入HashSet时,HashSet会先计算对象的hashcode值 来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode值作比较,HashSet如果没有相符的hashcode,会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。
从上面两点我们来分析如果不重写hashcode会有什么后果。
我们这样去利用一个map看下他的打印结果:
public class Person
{
private String name;
private int age;
private String sex;
Person(String name,int age,String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
}
/**
* @see Person
* @param args
*/
public static void main(String[] args)
{
HashMap<Person, Integer> map = new HashMap<Person, Integer>();
Person p = new Person("jack",22,"男");
Person p1 = new Person("jack",22,"男");
System.out.println("p的hashCode:"+p.hashCode());
System.out.println("p1的hashCode:"+p1.hashCode());
System.out.println(p.equals(p1));
System.out.println(p == p1);
map.put(p,888);
map.put(p1,888);
map.forEach((key,val)->{
System.out.println(key);
System.out.println(val);
});
}
p的hashCode:356573597
p1的hashCode:1735600054
false
false
com.blueskyli.练习.Person@677327b6
888
com.blueskyli.练习.Person@1540e19d
888
可以看到两个对象作为key值的时候,比较的hashcode实际上是堆上的内存地址。而如果我们想用name来做唯一性,需要先重写其equals
public class Person
{
private String name;
private int age;
private String sex;
Person(String name,int age,String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
@Override public boolean equals(Object obj)
{
if(obj instanceof Person){
Person person = (Person)obj;
return name.equals(person.name);
}
return super.equals(obj);
}
}
继续执行上面例子的打印,其结果如下:
其比较结果为:
p的hashCode:356573597
p1的hashCode:1735600054
true
false
com.blueskyli.练习.Person@677327b6
888
com.blueskyli.练习.Person@1540e19d
888
我们发现虽然我们已经重写了equals,但是其在hashmap中仍然设置进去了两个name值相同的对象。
如果我们想在map或者set中也根据name做其唯一性的话,必须重写hashcode。
public class Person
{
private String name;
private int age;
private String sex;
Person(String name,int age,String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
@Override public boolean equals(Object obj)
{
if(obj instanceof Person){
Person person = (Person)obj;
return name.equals(person.name);
}
return super.equals(obj);
}
@Override public int hashCode()
{
return name.hashCode();
}
}
p的hashCode:3254239
p1的hashCode:3254239
true
false
com.blueskyli.练习.Person@31a7df
888
总结:
- 两个对象,用==比较比较的是地址,需要采用equals方法(可根据需求重写)比较。
- 重写equals()方法就是重写hashcode()方法。
- 一般相等的对象都规定有相同的hashcode。
- String 类重写了equals和hashcode方法,比较的是值。
- 重写hashcode方法为了将数据存入Hashset/HashMap/Hashtable类时进行比较。