Understand the usage and precautions of Equals and HashCode at one time

Understand the usage and precautions of Equals and HashCode at one time

The concept and usage of Equals and HashCode have always been vague. Now I have time to collect some data and summarize them.

About Equals

The default implementation method in the Object class is: return this == obj. That is, true will be returned only if this and obj reference the same Object.

We often need to use equals to judge whether two objects are equivalent, rather than verify their uniqueness. In this way, when we implement our own classes, we need to rewrite equals. Of course, when we rewrite equals, we'd better rewrite the corresponding hashCode. Especially in the heap hash storage structure, such as hashSet,HashMap,HashTable and other related operations, java clearly stipulates that when we rewrite equal, we must rewrite hashCode. The following will explain this.

The Java language specification requires equals to have the following features:

    Reflexivity: for any non null reference x,x.equals(x) Should return true. 
    Symmetry: for any reference x and y,if and only if y.equals(x) return true,x.equals(y) Should also return true. 
    Transitivity: for any reference x,y and z,If x.equals(y)return true,y.equals(z) The same result should be returned.
    Consistency: if x and y The referenced object does not change and is called repeatedly x.equals(y) The same result should be returned.
    For any non empty reference x,x.equals(null) Should return false. 

When overriding the equals method, follow the above specifications, for example, the following usage

  public boolean equals(Object obj) {
            if (this == obj) return true;
            if ((obj==null)||(this.getClass()!=obj.getClass()))
                return  false;
                User user = (User) obj;
            return this.id==user.id&&this.name.equals(u ser.name);
        }

About HashCode

As for the function and precautions of HashCode, it is enough to read this article completely. I think the blogger's writing is very good after reading it. I recommend taking a look:
https://blog.csdn.net/lijiecao0226/article/details/24609559

Rewrite HashCode correctly

Reference documents: https://blog.csdn.net/benjaminzhang666/article/details/9468605#
Note: when rewriting the HashCode, you can't rewrite the same value. For two different objects (in this case, the internal member attribute values are different), it's unnecessary to compare their equals. It's better to take the member attributes into account when rewriting the HashCode, so you don't have to calculate their equals during the collection operation of hash storage structure, You can judge whether to add objects to the collection directly according to the HashCode. Of course, the more complex the HashCode is for objects with more members, which will also affect the performance

An example is given below

When overriding HashCode, it returns the same value

class  Student {
    private String name;
    private int age;

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

    public boolean equals(Object obj) {
        System.out.println(this.name +"Start comparison equals");
        if (this==obj)
            return true;
        if (obj!=null&&!(getClass()!=obj.getClass()))
            return false;
        Student student = (Student) obj;
        return this.name.equals(student.name)&&this.age==student.age;
    }

    public int hashCode(){
        System.out.println(this.name +"Start calculation HashCode");
        return 31*age;
    }
}


public class HashCodeAndEqualsTest {
    public static void main(String[] args) {
        Set<Student> students = new HashSet<>();
        Student student1 = new Student("green hand", 25);
        Student student2 = new Student("mogul", 25);
        students.add(student1);
        students.add(student2);

       System.out.println(students);
    }

}

Operation results:

The rookie began to calculate HashCode
 The boss began to calculate HashCode
 The big man began to compare equals
[Student{name='green hand', age=25}, Student{name='mogul', age=25}]

When overriding hashcode, all properties are counted and hashcode is modified

    public int hashCode(){
        System.out.println(this.name +"Start calculation HashCode");
        return 31*age+this.name.hashCode();
    }

Operation results:

The rookie began to calculate HashCode
 The boss began to calculate HashCode
[Student{name='mogul', age=25}, Student{name='green hand', age=25}]

It can be seen that for objects with different member variables, the second method calculates less equals than the first method, which improves the calculation performance

Summary:

  1. When overriding HashCode, you should not return the same value, but include the member properties of the class
  2. When rewriting HashCode, the member attributes of the class can be linearly combined, that is: hash = int form of attribute * * * * 1 + int form of C1 * attribute 2 + int form of C2 * attribute 3 +... (C1 and C2 are coefficients, which is clearer after learning linear algebra ~)
  3. Note that the value after splicing cannot exceed the expression range of shaping.
  4. For the reference property, you can directly call its HashCode, just like the string type, you can directly reference it
  5. Using linear combination can avoid hash conflict of different object parameters and improve the utilization of hash storage structure

Tags: Java hashcode

Posted on Mon, 20 Sep 2021 18:58:16 -0400 by sharugan