Why should override the equals method and hashcode methods

We may often hear that override equals method must override hashcode method, this is why? All java Object classes are subclasses, directly on the source object

 1 /*
 2  * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
 3  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 4  *
 5  */
 6  
 7 package java.lang;
 8  
 9 /**
10  * Class {@code Object} is the root of the class hierarchy.
11  * Every class has {@code Object} as a superclass. All objects,
12  * including arrays, implement the methods of this class.
13  *
14  * @author  unascribed
15  * @see     java.lang.Class
16  * @since   JDK1.0
17  */
18 public class Object {
19  
20     private static native void registerNatives();
21     static {
22         registerNatives();
23     }
24     public final native Class<?> getClass();
25  
26      
27     public native int hashCode();
28  
29      
30     public boolean equals(Object obj) {
31         return (this == obj);
32     }
33  
34     
35     protected native Object clone() throws CloneNotSupportedException;
36  
37     public String toString() {
38         return getClass().getName() + "@" + Integer.toHexString(hashCode());
39     }
40  
41     public final native void notify();
42  
43  
44     public final native void notifyAll();
45  
46  
47     public final native void wait(long timeout) throws InterruptedException;
48  
49  
50     public final void wait(long timeout, int nanos) throws InterruptedException {
51         if (timeout < 0) {
52             throw new IllegalArgumentException("timeout value is negative");
53         }
54  
55         if (nanos < 0 || nanos > 999999) {
56             throw new IllegalArgumentException(
57                                 "nanosecond timeout value out of range");
58         }
59  
60         if (nanos > 0) {
61             timeout++;
62         }
63  
64         wait(timeout);
65     }
66  
67     public final void wait() throws InterruptedException {
68         wait(0);
69     }
70  
71     protected void finalize() throws Throwable { }

First, let's review under the hash algorithm and hashmap

In a length of n (assuming 10,000) linear table (assuming the ArrayList), the digital storage disordered; If we are looking for a specified number, would have to look through from beginning to end, traversing, such Find the average number n is divided by 2 (here, 5000).

Let us observe Hash table (Hash table here is purely conceptual data structures, and Java has nothing to do). Its average search times close to 1, the cost is quite small, the key is in the Hash table, stored therein data and its storage location is associated with the Hash function.

We assume a Hash function is x * x% 5. Of course, in reality impossible to use such a simple Hash function, we are here purely for convenience of explanation, while the Hash table is a linear table length is 11. 6 If we want to put into it, then we will first use 6 Hash function calculation, the result is 1, so we'll put the index number is 6 1 this position. Similarly, if we want to put the number 7, after Hash function calculation, the result is 7 4, it will be included in the index is the position 4. The results as shown in FIG.

 

 

The benefit of this is obvious. For instance, we find from 6 in this element, we can index calculated by the position 6 of the first Hash function, and then find it directly from No. 1 in the index.

However, we encounter the "Hash values ​​conflict," this issue. After such calculation Hash function, 7 and 8 have the same Hash value, which uses the Java HashMap object is "strand address method" solution. Results as shown below.

 

Specific approach is for all Hash value is an object i build a synonym list. Suppose we put the time in 8 Discovery 4 position has been accounted for, it will create a new node into the linked list 8. Similarly, if we are looking for 8, then No. 4 found in the index instead of 8, and that will in turn look down the list.

Although we still can not completely avoid the problem of conflict of Hash values, but Hash function design is reasonable, it can still ensure a synonym list length is controlled in a reasonable range. Speaking of theoretical knowledge are not random, we can clearly understand the importance rewrite hashCode method in later years.

A simple example

  When we deposit a custom class with a HashMap, if they do not override the equals and hashCode methods from the class definition, and the result will be not the same as we expected. We look WithoutHashCode.java this example.

In the second line 18 to which, we define a class Key; in line 3 which defines a unique attribute id. Currently we first commented hashCode method on line 9 equals methods, and on line 16.    

. 1    Import the java.util.HashMap;
 2    class Key {
 . 3        Private Integer ID;
 . 4        public Integer getId ()
 . 5 { return ID;}
 . 6        public Key (Integer ID)
 . 7 { the this .id = ID;}
 . 8    // intentionally first Comment out equals and hashCode method 
. 9    //   public Boolean equals (Object O) { 
10   //       IF (O == null || (the instanceof O Key)!) 
. 11   //       {return to false;} 
12 is   //       the else 
13 is   //      { return this.getId().equals(((Key) o).getId());}
14  //  }
15    
16  //  public int hashCode()
17  //  { return id.hashCode(); }
18  }
19
20  public class WithoutHashCode {
21      public static void main(String[] args) {
22          Key k1 = new Key(1);
23          Key k2 = new Key(1);
24          HashMap<Key,String> hm = new HashMap<Key,String>();
25          hm.put(k1, "Key with id is 1");   
26          System.out.println(hm.get(k2));   
27      }
28  }

 

In the main function of the first 22 and line 23, we define two Key objects whose id is 1, like they are the same two keys can open the same door.   

In line 24, we create a HashMap object by generics. It can store the object key portion Key type, value, may be stored in an object of type String.

    In line 25, we put the method by k1 and the string of characters to put in hm; and in line 26, we want to get value from HashMap k2 Lane; It's like we want to use this key to k1 lock the door, came to the door with k2. This is logical, but the current results, returns the string result is not what we imagined line 26, but null.

    There are two reasons - not rewritten. The first is not override hashCode method, there is no override equals the second method.

   When we put k1 to HashMap, the hashCode method first calls the Key of this class compute its hash value, then put into the hash value of k1 guided by the memory location.

    The key is we do not define the hashCode method Key in. Here still call the hashCode method (all classes are subclasses of Object) Object class, and the class Object hashCode method returns the hash value is actually a memory address k1 object (assumed to be 1000).

    

    If we followed calls hm.get (k1), then we will call again hashCode method (k1 or return address of 1000), then in accordance with the hash value obtained k1 can be found quickly.

    But here's the code we are hm.get (k2), when we call the hashCode method of the Object class when (as in not defined Key) calculated hash value k2, in fact, get the memory address of k2 (assumed to be 2000). Since k1 and k2 are two different objects, they will not be the same memory address, which means that their hash values ​​must be different, that's the reason we can not get with the hash value of k1 and k2.

    When we annotated hashCode method of rows 16 and 17 removed, will find that it is the return value of the id attribute hashCode, where k1 and k2 are the id of 1, so their hash values ​​are equal.

    Let us correct what kept k1 and k2 take action. When deposit k1, based on the hash value of its id, is assumed 100 here, k1 object into the corresponding position. While taking k2, it is to calculate a hash value (k2 because the id is 1, this value is 100), then go to this position.

    But the result will be our surprise: No. 100 position obviously has k1, but the output line 26 is still null. The reason is that no object equals method Key rewrite.

    HashMap is treated by the conflict chain address method, that is, at positions 100, there may exist a plurality of objects stored in a linked list. They returned by the hash values ​​are 100 hashCode method.

     When we look through the hashCode to 100 position of k2, does get k1. But k1 and k2 is possible only with the same hash value, but not necessarily equal, and k2 (k1 and k2 two keys may not be able to open the same door), this time, we need to call the equals method Key object to determine whether the two the equal.

    Since we do not define the equals method in Key object, the system would have to call the equals method of the Object class. Due to the inherent method of Object is judged according to the memory address of the two objects, so k1 and k2 will not be equal, which is why it is still in line 26 through hm.get (k2) still give reason to null.

Why override equals method in general must override the hashcode method

First, look at the source of the above-mentioned integer

@Override
public int hashCode() {
    return Integer.hashCode(value);
}
 
public static int hashCode(int value) {
    return value;
}
 
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

 

  The Integer class equals method and hashcode methods are rewritable, hashcode method Integer class is the return value itself, equals the value of the comparative method itself are equal.

  The equals method must meet the following characteristics

  1. reflexive: x.equals (x) == true, themselves and their comparison equal

  2. Symmetry: x.equals (y) == y.equals (x), two objects call equals the result should be the same

  3. transitive: if x.equals (y) == true y.equals (z) == true then x.equals (z) == true, and x is equal to y, y and z are equal, x and z is equal to

  4. Consistency: If x and y target object has member variables num1 and num2, equals method in which overridden only num1 took part in the operation, the modification does not affect the value of num2 x.equals (y) of

  And then if a class does not override the hashcode method, then, equals two determination values ​​are equal, but the values ​​are not equal hashcode, such as the String class, which would cause ambiguity

Guess you like

Origin www.cnblogs.com/coder-wf/p/12189491.html