Java常用API—— 重写对象的equals和hashCode方法

Posted Johnny*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java常用API—— 重写对象的equals和hashCode方法相关的知识,希望对你有一定的参考价值。

未重写前,调用父类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 方法

以上是关于Java常用API—— 重写对象的equals和hashCode方法的主要内容,如果未能解决你的问题,请参考以下文章

Object类的equals方法 常用API

java中重写Object类的equals方法为啥要重写hashcode方法?不重写可以吗?

常用API二

如何重写hashCode()和equals()方法

equals和==的区别

请问各位大佬这道java的重写equals和重写hashcode方法内部是啥意思呀?