在java笔试和面试中,经常会遇到“重写equals方法是否要重写hashCode方法“的问题。正好最近看到《effective java》中的这个地方,标题就是“覆盖equals方法总要覆盖hashCode方法”。
先看代码,本地也写了个demo,创建一个对象,先不重写hashCode方法,然后结合基于散列的集合使用,对于满足equals方法的相等的对象,看是否真的是相等的对象。
这段代码没有重写hashCode方法:
class TestObj {
int i, j;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public int getJ() {
return j;
}
public void setJ(int j) {
this.j = j;
}
TestObj(int i, int j) {
super();
this.i = i;
this.j = j;
}
@Override
public boolean equals(Object obj) {
if (this == obj){
return true;
}
if (obj == null){
return false;
}
if(!(obj instanceof TestObj)){
return false;
}
TestObj other = (TestObj) obj;
if (i == other.i && j == other.j){
return true;
}
return true;
}
}
接下来我们使用这个TestObj对象,书写一个测试类:
@Test
public void Test1(){
Map<TestObj, String> map = new HashMap<TestObj, String>();
TestObj t1 = new TestObj(1, 2);
TestObj t2 = new TestObj(1, 2);
map.put(t1, "obj");
System.out.println(t1.equals(t2));
System.out.println("t1-->" + t1.hashCode());
System.out.println("t1-->" + t2.hashCode());
System.out.println(map.get(t2));
}
控制台输出结果是“true”、“null”。t1和t2是满足equals方法的相同的对象,由于没有重写hashCode方法,打印出来的hashCode的值也不同,对象的散列码不同,从而获取的值为空。下面把这个类的hashCode的方法重写,如下:
@Override
public int hashCode() {
final int f = 31;
int result = 1;
result = f * result + i;
result = f * result + j;
return result;
}
添加了重写hashCode方法后,在运行控制台输出的结果是“true”、“obj”。此时是真正意义的相等,相等的对象实例具有相同的散列码。
在Object规范中,有这样的判定描述:
1、在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一的返回同一个整数。在一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。
2、如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
3、如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生不同的整数结果。
由此可见,在没有重写hashCode方法,即使实例对象equals方法是相等的,由于两者的hashCode值不同,所两个实例对象并不是相等的,所以获取到的值是为空。只有在重写了hashCode后,能够获取到对应的value值。
又写了一个Test2,此时是TestObj类是没有重写hashCode方法的,在map的添加角度也可以看出,代码如下:
@Test
public void Test2(){
Map<TestObj, String> map = new HashMap<TestObj, String>();
TestObj t1 = new TestObj(1, 2);
TestObj t2 = new TestObj(1, 2);
map.put(t1, "obj");
map.put(t2, "obj2");
System.out.println(t1.equals(t2));
System.out.println("t1-->" + t1.hashCode());
System.out.println("t1-->" + t2.hashCode());
System.out.println(map.get(t2));
System.out.println(map.size());
}
我们知道map集合的key是唯一的,即使在添加相同的key,map只会覆盖最后一次添加key对应的value值。上面的案例,没有重写TestObj类的hashCode方法,控制台的输出依次是“true”、“t1-->1245607833”、“t1-->1260258275”、“obj2”、“2”,可以看出,map集合是人为两个对象并不相等,对于hashCode可以看出,确实不相等,所以打印出来的长度为2.
接下来加上TestObj类的hashCode方法,这是打印的两者的hashCode值是相等的,map的长度是1。这样就对了,两个相等的对象,map添加是取最后一次对应value值。
理论上来说,在比较对象是否相等时,是先比较两者的hashCode值相等,如果hashCode的值相等,那么两个对象是相等的,相反,hashCode值不等,两个对象也就不等。当然,这个要看你的hashCode方法是怎么写的。