未重写前,调用父类Object的equals方法,该方法比较的是引用对象存储的值:即对象在堆中的地址。
package com.JavaSe.eq;
class Line{
double k, b;
public Line(double kk, double bb){
this.k = kk;
this.b = bb;
}
}
public class TestEq {
public static void main(String[] args) {
Line line1 = new Line(1.5, 2.2);
Line line2 = new Line(1.5, 2.2);
System.out.println(line1.equals(line2));//false
}
}
只重写equals不重写hashCode
package com.JavaSe.eq;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
class Line{
double k, b;
public Line(double kk, double bb){
this.k = kk;
this.b = bb;
}
//重写equals方法: 比较k和b是否相等
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Line line = (Line) o;
return Double.compare(line.k, k) == 0 &&
Double.compare(line.b, b) == 0;
}
@Override
public String toString() {
return "Line{" +
"k=" + k +
", b=" + b +
'}';
}
}
public class TestEq {
public static void main(String[] args) {
Line line1 = new Line(1.5, 2.2);
Line line2 = new Line(1.5, 2.2);
System.out.println(line1.equals(line2));//重写完之后比较结果为:true
Set<Line> set = new HashSet<>();
set.add(line1);
set.add(line2);
System.out.println(set); // [Line{k=1.5, b=2.2}, Line{k=1.5, b=2.2}]
System.out.println( line1.hashCode() == line2.hashCode()); //false
}
}
可以发现,equals比较的结果为true,我们认定相同{k,b}为同一对象,这符合我们的要求。但是使用HashSet添加上述两个对象的时候,居然都添加进去了。这显然不符合我们的预期。这是因为hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。而上述对象没有重写hasCode方法,两个对象的hashCode值不一样,也就被认定为两个不同的对象了。
通过看 hashSet 的 add 方法能够得知 add 方法里面使用了对象的 hashCode 方法来判断,所以我们需要重写 hashCode 方法来达到我们想要的效果。
重写equals和hashCode
如何计算对象的hashCode
生成一个 int 类型的变量 result,并且初始化一个值,比如17。
对类中每一个重要字段,也就是影响对象的值的字段,也就是 equals 方法里有比较的字段,进行以下操作:
a. 计算这个字段的值 filedHashValue = filed.hashCode();
b. 执行 result = 31 * result + filedHashValue;
package com.JavaSe.eq;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
class Line{
double k, b;
public Line(double kk, double bb){
this.k = kk;
this.b = bb;
}
//重写equals方法: 比较k和b是否相等
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Line line = (Line) o;
return Double.compare(line.k, k) == 0 &&
Double.compare(line.b, b) == 0;
}
//计算HashCode
@Override
public int hashCode() {
return Objects.hash(k, b);
}
@Override
public String toString() {
return "Line{" +
"k=" + k +
", b=" + b +
'}';
}
}
public class TestEq {
public static void main(String[] args) {
Line line1 = new Line(1.5, 2.2);
Line line2 = new Line(1.5, 2.2);
System.out.println(line1.equals(line2));//重写完之后比较结果为:true
Set<Line> set = new HashSet<>();
set.add(line1);
set.add(line2);
System.out.println(set); //[Line{k=1.5, b=2.2}]
System.out.println( line1.hashCode() == line2.hashCode()); //true
}
}
参照Objects.hash(k, b)的源码,hashCode计算方法也可写成如下:
@Override
public int hashCode() {
//使用包装类
Double kk = this.k;
Double bb = this.b;
int result = 1;
result = 31 * result + (kk == null ? 0 : kk.hashCode());
result = 31 * result + (bb == null ? 0 : bb.hashCode());
return result;
}
参考文章:
Java 如何重写对象的 equals 方法和 hashCode 方法