Understand the usage and precautions of Equals and HashCode at one time! Interview must ask the basics! !

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

The concepts and usage of Equals and HashCode have always been very vague. Now I have time to collect some information and summarize them.

About Equals

The default implementation in the Object class is: return this == obj . That is to say, it will only return true if this and obj refer to the same object.

And we often need to use equals to judge whether two objects are equivalent, rather than verifying their uniqueness. In this way, when we implement our own classes, we must rewrite equals. Of course, when rewriting equals, it is better to rewrite the corresponding hashCode, especially when the heap hash storage structure, such as hashSet, HashMap, HashTable and other related operations, Then java clearly stipulates that rewriting equal must rewrite hashCode, which will be explained below.

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

    自反性:对于任何非空引用 x,x.equals(x) 应该返回 true。
    对称性:对于任何引用 x 和 y,当且仅当 y.equals(x) 返回 true,x.equals(y) 也应该返回 true。
    传递性:对于任何引用 x、y 和 z,如果 x.equals(y)返回 true,y.equals(z) 也应返回同样的结果。
    一致性:如果 x 和 y 引用的对象没有发生变化,反复调用 x.equals(y) 应该返回同样的结果。
    对于任意非空引用 x,x.equals(null) 应该返回 false

Follow the above specifications when overriding the equals method, 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

Regarding the role and precautions of HashCode, it is enough to read this complete article. I think after reading it, I feel that this blogger has written it well. I recommend reading it :
https://blog.csdn.net/lijiecao0226/article/details /24609559

Correctly rewrite HashCode

Reference document: https://blog.csdn.net/benjaminzhang666/article/details/9468605#
Note: When rewriting HashCode, it cannot be rewritten to the same value, for two different objects (here refers to the internal member attribute value is different The same), then there is no need to compare its equals, it is better to try to calculate the member attributes as much as possible when rewriting HashCode, so that when performing the set operation of the hash storage structure, it is not necessary to calculate its equals, You can directly judge whether to add an object to the collection based on HashCode. Of course, for objects with more members, the HashCode is more complicated, which will also affect performance.

An example is given below

Returns the same value when overriding HashCode

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 +"开始比较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 +"开始计算HashCode");
        return 31*age;
    }
}


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

       System.out.println(students);
    }

}

operation result:

菜鸟开始计算HashCode
大佬开始计算HashCode
大佬开始比较equals
[Student{
    
    name='菜鸟', age=25}, Student{
    
    name='大佬', age=25}]

When rewriting HashCode, all attributes are counted, and hashCode is modified

    public int hashCode(){
    
    
        System.out.println(this.name +"开始计算HashCode");
        return 31*age+this.name.hashCode();
    }

operation result:

菜鸟开始计算HashCode
大佬开始计算HashCode
[Student{
    
    name='大佬', age=25}, Student{
    
    name='菜鸟', 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

Summarize:

  1. When overriding HashCode, it should not return the same value, but should 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 + C1* int form of attribute 2 + C2* int form of attribute 3 + ... (C1, C2 For the coefficient, it is more clear for those who have learned linear algebra~)
  3. Note that the value after concatenation cannot exceed the expression range of the integer.
  4. For the reference property, you can directly call its HashCode, just like the string type, you can directly use the reference
  5. Using linear combination can avoid hash conflict of different object parameters and improve the utilization of hash storage structure

Guess you like

Origin blog.csdn.net/qq_38338409/article/details/120391220