很多人在重写equals时,都会看到网上一般推荐同时重写hashCode,为什么呢?一开始我以为equals中比较的是hashCode,查了下源码发现并不是这么回事,那么到底为什么会有这么一个推荐呢?
hashCode值代表的含义
首先,我们要知道hashcode值不是内存地址,hashcode值是对内存地址进行一些算法操作后得到的值,可能多个内存地址对应一个hashcode值,比如整数的取模(16%9=7,25%9=7,最后16和25具有相同的hashcode值),那为什么要对内存地址进行一些算法操作呢?这是因为到像hashMap之类的数据结构中添加数据,而hashmap中已经存有非常多的数据时,如果没有hashcode值,我们就得一个个遍历所有的数据看是否含有相同的数据,这样的效率特别低下,但当你使用hashcode值比较的话,你只需比较具有相同hashcode值的数据即可,这就使得效率大大提高;equals()和hashCode()的区别
上面我们知道一个hashcode值可能对应多个内存地址。也就是说内存地址不同,但hash值可能相同,但hash值不同,内存地址一定不同。所以比较对象时,先通过hashCode()比较,如果不等则两对象一定不同,如果相等再通过equals()比较,如果相等则两对象相同,如果不等则两对象不同。重写
而String重写了equals同时重写了hashCode,重写hashCode代码如下:
通过源码可以看到String类的hashCode是通过对象的值来确定的,而String类中equals比较的就是值,而不是地址。这就保证了String重写equals时equals和hashCode的一致性。
我们来看一段代码:
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
o1 = "sss";
o2 = "sss";
System.out.println(o1.equals(o2));
System.out.println("o1.hashCode():" + o1.hashCode());
System.out.println("o2.hashCode():" + o2.hashCode());
}
结果为:
我们可以看到当equals相同时,对应的hashCode也是一样的,这就是equals和hashCode的一致性。为什么会存在这种一致性呢?那是因为像hashMap,hashSet这些集合框架,当你重写了equals方法而没有重写hashCode()时,可能会出现equals相同,而hashCode却是不同的,然后在这些集合框架对hashCode比较时,显示false,这和我们的预期存在差入。
重写equals而不重写hashCode
@Override
public boolean equals (Object o) {
if (o instanceof Student) {
Student s = (Student) o;
if (this.sName.equals(s.sName)) return true;
else return false;
}
return false;
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
s1.setsName("sss");
s2.setsName("sss");
System.out.println(s1.equals(s2));
System.out.println("s1.hashCode():" + s1.hashCode());
System.out.println("s2.hashCode():" + s2.hashCode());
}
结果为:
从结果看没重写hashCode()时,本来我们是想两个age相同的hashCode应该是一样,可hashCode是不同的地址。而如果是hashMap或hashSet这些集合框架时,就会产生了一些错误。像hashSet去重的话,我们是想将age相同的去重,但是从上面我们知道两个age相同,但hashCode不同,而去重是对hashCode的比较,最后也就达不到我们想要的去重效果。
即重写equals又重写hashCode
@Override
public int hashCode () {
return this.sName.hashCode();
}
@Override
public boolean equals (Object o) {
if (o instanceof Student) {
Student s = (Student) o;
if (this.sName.equals(s.sName)) return true;
else return false;
}
return false;
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
s1.setsName("sss");
s2.setsName("sss");
System.out.println(s1.equals(s2));
System.out.println("s1.hashCode():" + s1.hashCode());
System.out.println("s2.hashCode():" + s2.hashCode());
}
结果为:
我们可以看到equals返回true,而且hashCode的地址也是一样的,这就符合之前所说的equals和hashCode的一致性。完美。这个时候你就没必要担心一些比较会出现问题了。所以说当你重写equals时,最好将hashCode也跟着一起重写。当然如果你非常确定你不会使用到hashCode比较,也是可以不重写hashCode的。