javaGuide里说到了为什么要重写hashcode的原因:
为什么重写 equals 时必须重写 hashCode 方法?
如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。
hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
但是我没太理解,两个对象有相同的code他们不一定是相等的又咋样,为什么就要重写hashcode?
后面自己看了别的博文,理解了下,我觉得一定要重写hashcode的主要原因是要保障equals方法的特性,即equals返回结果必须与其hashcode比较结果必须保持一致
.
为什么要这样保障呢?
1.了解hashcode是干啥的
hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
2.了解一个过程
- 1.确定和保障对象的唯一性,我们在使用set和map的时候有下面这样一个先hashcode确定其唯一性的过程
当你把对象加入 HashSet 时,HashSet 会
先计算对象的 hashcode 值
来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的 Java 启蒙书《Head First Java》第二版)。,为什么呢 ?这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
从上面两点我们来分析如果不重写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 map = new HashMap();
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
我们发现虽然我们已经重写了equlas,但是其在hashmap中仍然设置进去了两个name值相同的对象
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();
}
}
如果我们想在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
总结:
1,两个对象,用==比较比较的是地址,需采用equals方法(可根据需求重写)比较。
2,重写equals()方法就重写hashCode()方法。
3,一般相等的对象都规定有相同的hashCode。
4,String类重写了equals和hashCode方法,比较的是值。
5,重写hashcode方法为了将数据存入HashSet/HashMap/Hashtable(可以参考源码有助于理解)类时进- 行比较