Why is it required to override the hashCode method when overriding the equals method?

1 equals method

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 class, we must override equals.

 

By convention, equals must satisfy the following rules.

  • Reflexivity : x.equals(x) must be true
  • For null : x.equals(null) must be false
  • Symmetry : x.equals(y) and y.equals(x) have the same result
  • Transitivity : a and b equals, b and c equals, then a and c must also be equals.
  • Consistency : During a runtime, changes in the state of the two objects will not affect the decision result of equals, then, during this runtime, no matter how many times equals is called, the same result is returned.

 

Example: Overriding the equals method

1  class Test
 2  {
 3      private  int num;
 4      private String data;
 5  
6      public  boolean equals(Object obj)
 7      {
 8          if ( this == obj)
 9              return  true ;
 10  
11          if ((obj == null ) || (obj.getClass() != this .getClass()))
 12              return  false ;
 13  
14          // It can be executed here, indicating that obj and this are the same and not null. 
15         Test test = (Test) obj;
 16          return num == test.num&& (data == test.data || (data != null && data.equals(test.data)));
 17      }
 18  
19      public  int hashCode( )
 20      {
 21          // Rewrite equals, you must also rewrite hashCode. Details will be introduced later. 
22      }
 23  
24 }

 

2 hashCode method

This method returns the hash code of the object, the return value is the hash code of type int.
The hash code of the object is to better support the Java collection classes based on the hash mechanism, such as Hashtable, HashMap, HashSet, etc.


Regarding the hashCode method, the consistent convention is:

  • During a runtime period, as long as the object's (field) changes do not affect the equals method's decision result, then no matter how many times hashCode is called during this period, the same hash code must be returned.
  • If 2 objects return true after calling equals, then the has of these 2 objects
  • The hCode method must also return the same int hash code. If two objects return false via equals, their hashCode returns are allowed to be the same . (However, programmers must be aware that hashCode returns a unique hash code, which makes the hashtables that store this object work better.)

Objects that override the euqls method must also override the hashCode() method.

 

3 Why must the hashCode method be overridden?

In the above example, the Test class object has two fields, num and data, these two fields represent the state of the object, and they are also used in the equals method as the basis for judgment. Then, in the hashCode method, these two fields also participate in the operation of the hash value as the intermediate parameters of the hash operation. This is very important, this is to comply with: 2 objects are equals, then the hashCode must be the same rule.

That is to say, the fields participating in the equals function must also participate in the calculation of hashCode.

 

4 Notes when rewriting hashCode

When overriding the hashCode method, in addition to the above consistency conventions, the following points need to be paid attention to:

(1) The returned hash value is of type int to prevent overflow.

(2) The hash values ​​returned by different objects should be as different as possible. (For the efficiency of collections such as hashMap)

(3) A situation is mentioned in "Java Programming Ideas"

" The most important factor in designing hashCode() is that whenever you call hashCode() on the same object, it should yield the same value. If an object is added to a HashMap with put() to yield a hashCdoe value, and When using get(), another hashCode value is generated, so the object cannot be obtained. So if your hashCode method depends on the volatile data in the object, the user should be careful, because when this data changes, The hashCode() method will generate a different hash code".

for example

public class Test {
    
    private int num;
    private String data;
    
    public Test(int num,String data){
        this.num = num;
        this.data = data;
    }
    
    public  void setNum( int num) {
         this .num = num;
    }

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

        if ((obj == null) || (obj.getClass() != this.getClass()))
            return false;

        Test test = (Test) obj;
        return num == test.num&& (data == test.data || (data != null && data.equals(test.data)));
    }

    public int hashCode()
    {
        int hash = 7;
        hash = 31*hash+num;
        hash = 31*hash+data.hashCode();
        return hash;
        
    }
    
    public static void main(String[] args) {
        
        Map<Test,Integer> map = new HashMap<>();
        Test t1 = new Test(21,"ouym");
        map.put(t1, 1);
        t1.setNum ( 20 );
        System.out.println(map.get(t1));
        
    }

}

The output value is null, my god, the hashMap can't get the value.

If the equals and hashCode methods are not overridden, it does not depend on the changes of the object properties, that is to say, the default hashCode method can be used here to get the value. But the original intention of rewriting the equal method is to determine that Test objects with equal name and num attributes are equal, not that the references to the same object are equal, and num=21 and num=20 obviously do not want to wait, so here hashCode returns Different values ​​do not violate the original intention of the design. Note the usage pitfalls of the above code.

 

 

 

From: https://www.cnblogs.com/lulipro/p/5628750.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324970310&siteId=291194637