Java进行对象比较时的equals()和hashcode()方法

Posted akyna-zh

tags:

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

Java进行对象比较中的equals()和hashcode()

1.equals()方法与"=="的比较与分析

首先提出一个误区:

  • == 比较时进行地址的比较
  • equals 比较时进行值的比较
String a = "123";
String b = "123";
System.out.println(a.equals(b)); // true
System.out.println(a == b); // true

String a1 = new String("123");
String b1 = new String("123");
System.out.println(a1.equals(b1)); // true
System.out.println(a1 == b1); // false

好像没问题。

然而阅读源码Object类,我发现对象中默认equals方法进行的是地址的比较

public boolean equals(Object obj) {
    return (this == obj);
}

再阅读源码String类,可以发现String类中重写了equals方法,覆盖掉了Object的equals方法,所以String的equals是值比较!

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String aString = (String)anObject;
        if (coder() == aString.coder()) {
            return isLatin1() ? StringLatin1.equals(value, aString.value)
                              : StringUTF16.equals(value, aString.value);
        }
    }
    return false;
}

所以equals()不一定进行值的比较,在使用自己创建的类时,若想进行值的比较就必须重写equals()方法。

2.应该如何重写equals方法

  • (1)重写equals方法的目的

重写equals是 在两个对象某些字段相等时就认定两个对象相等 的情况下进行的

  • (2)重写一个完美的equals()的步骤

1.显式参数命名为otherObject,稍后将它强制转换为另一个名为other的变量
2.检测this与otherObject是否相等
3.检测otherObject是否为null
4.比较this与otherObject的类,如果equals的语义可以在子类中改变,就使用getclass检测
如果所有的子类都有相同的相等性语义,就使用instanceof检测
5.将otherObject强制转换为相应类类型的变量
6.使用 == 比较基本类型字段 使用 Object.equals比较对象字段

by 《core Java》

注:

instanceof进行类型检查规则是:你属于该类or该类的派生类;
getClass获得类型信息采用==来进行检查是否相等的操作是严格的判断,只会判断”本类”.

example:

当两个人年龄相同时,我们认为这两个对象相同:

class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age;
    }

    @Override
    public int hashCode() {
        return Objects.hash(age);
    }
}

test:

Person p1 = new Person(15, "Mary");
Person p2 = new Person(15, "Mark");
System.out.println(p1.equals(p2)); // true
System.out.println(p1 == p2); // false

3.为什么重写equals()时要重写hashcode()

可以看到上述程序中不但重写了equals()方法,同时重写了hashcode()方法:

@Override
public int hashCode() {
    return Objects.hash(age);
}

重写equals最好要重写hashcode(),对需要进行比较的部分字段进行hash(),也就是让值通过一个哈希函数获取到对应的哈希值

为何?

其实这样是为了提高程序的效率:

如果不重写hashcode()的话,可以试想一下,向一个元素个数为10000的集合中插入一个新的元素,就需要对之前的10000个元素进行比较,每次比较都要调用一次equals()比较每一个需要比较的字段,可以容易地想象出比较需要花费的时间复杂度是巨大的。

如果插入元素时,先通过hashcode的寻找可以飞速地判断集合中是否存在一样的元素,在O(1)的时间内可以解决集合插入问题。

同时重写hashcode()也保证了对象进行equals时,如果为true,其对应的hashcode也一定相等。虽然在不涉及集合操作时不重写hashcode()不影响比较,编译运行也不会报错,但是这会违反Java中对象的比较规则。

关于这两个方法的重要规范:

规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。

规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode可能相同”。

over

以上是关于Java进行对象比较时的equals()和hashcode()方法的主要内容,如果未能解决你的问题,请参考以下文章

Java中equals,hashcode,==的区别

HashMap理解

HashMap理解

Java为什么要同时重写equals和hashcode

hashcode()和equals()的作用区别联系

equals和HashCode深入理解以及Hash算法原理