看了《effective java》第九条。特此对equals方法和hashCode方法做此总结
现在假设我们有一个矩形类Rectangle
public class Rectangle {
private int length;
private int width;
public Rectangle(int length, int width) {
this.length = length;
this.width = width;
}
}
先回顾下我们为什么要覆盖equals方法呢?现在我们有两个矩形类的对象实例rectangle1 和rectangle2。
Rectangle rectangle1 = new Rectangle(3, 4);
Rectangle rectangle2 = new Rectangle(4, 3);
我想知道两个矩形是否是相等的,如果使用==运算符的话,比较的是rectangle1的引用和rectangle2的引用分别指向的对象在堆中存储的位置。两个对象是先后new出来的,所以肯定是在堆中有两个不同的对象,得到的结果就必然是false。
System.out.println(rectangle1 == rectangle2);
输出结果:false
这并不是我们想要的,我们的目的并不是想知道这两个引用是否是指向同一个对象,而是想知道这两个矩形的长宽是否相同,简单来说我们想要的是通过逻辑判断来得知这两个矩形rectangle1和rectangle2是否相等。所以我们通过覆盖equals方法来实现。
@Override
public boolean equals(Object obj) {
Rectangle o = (Rectangle) obj;
if (length == o.length){
if (width == o.width){
return true;
}else return false;
} else if (width == o.length) {
if (length == o.width) {
return true;
}else return false;
}
return false;
}
这个equals方法中,我们制定的规则是两个矩形,有相同的长、宽,即认为两个矩形是相同的。
这时再通过equals方法来判断两个矩形是否相等
System.out.println(rectangle1.equals(rectangle2));
输出结果: true
到这里我们已经满足了我们判断两个矩形是否相同的需求了。
那我们为什么还要覆盖hashCode方法呢?
如果这个矩形类是需要放进类似HashMap之类的基于哈希表来实现的容器中并作为key时,就有必要覆盖hashCode方法,按照我们自定的规则来得到一个矩形对象的hash值。(如果不指定,默认一般是通过将该对象的内部地址转换成一个整数来实现的)
HashMap<Rectangle, String> map2 = new HashMap<>(16);
map2.put(rectangle1, "nice");
map2.put(rectangle2, "bad");
System.out.println("size:"+map2.size());
输出结果:size:2
我们已经认为rectangle1和rectangle2在逻辑上是相同的了,也就是rectangle1和rectangle2是同一个key了,在put方法添加rectangle2的时候,“bad”理应覆盖掉“nice”,size应该为1,但事实却并发我们想的这样,哈希表在计算数组下标的时候,是会调用此对象的hashCode方法,如果我们没有覆盖hashCode方法,计算hash值时则会将该对象的堆地址转换成一个整数hash值,这在很大概率上是会将rectangle1和rectangle2分配在两个不同的散列桶中。
想要让rectangle1和rectangle2做为同一个key,在散列时落在同一个桶中,我们就必须要来覆盖hashCode方法
@Override
public int hashCode() {
//以下的17、31都是任意指定大小的数字。
int result = 17;
//这里我简单指定面积相同的矩形,hashCode值就相同
result = 31 * result + length*width;
return result;
}
这时再试一次
HashMap<Rectangle, String> map2 = new HashMap<>(16);
map2.put(rectangle1, "nice");
map2.put(rectangle2, "nice");
System.out.println("size:"+map2.size());
输出结果:size:1
这下得到了我们想要的结果。
我们在覆盖了hashCode方法之后,要问问自己”我们认为逻辑相等的实例是否都具有相等的散列码”。通过编写单元测试来验证一下你的推断。
总结:需要做逻辑相等判断的类,覆盖equals方法,如果还需要在散列表(HashMap、HashSet)中作为key,需要覆盖hashcode方法。
文章有些粗糙,如果错误,欢迎指出~~