eighth week java ee homework

1. Introduction of hashcode() method and equals() method

Java's base class Object provides some methods. The equals() method is used to determine whether two objects are equal, and the hashCode() method is used to calculate the hash code of the object. Neither equals() nor hashCode() are final methods and can be overwritten.

1. equal() method

 The equals() method in the Object class is implemented as follows:

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

It can be seen from this implementation that the implementation of the Object class adopts the algorithm with the highest degree of discrimination, that is, as long as the two objects are not the same object, equals() must return false.

Although we can override the equals() method when defining a class, there are some caveats; the JDK specifies the conventions that should be followed when implementing the equals() method:

(1) Reflexivity: x.equals(x) must return true.

(2) Symmetry: The return values ​​of x.equals(y) and y.equals(x) must be equal.

(3) Transitivity: x.equals(y) is true, y.equals(z) is also true, then x.equals(z) must be true.

(4) Consistency: If neither the information used by objects x nor y in equals() has changed, then the value of x.equals(y) remains unchanged.

(5) Non-null: x is not null and y is null, then x.equals(y) must be false.

 

2.hashCode() method

(1) Object's hashCode():

The declaration of the hashCode() method in the Object class is as follows:

            public native int hashCode();

It can be seen that hashCode() is a native method, and the return value type is an integer; in fact, the native method returns the address of the object in memory as a hash code, which can ensure that the return value of different objects is different.

Similar to the equals() method, the hashCode() method can be overridden. The role of the hashCode() method in JDK and the precautions for implementation are explained:

(1) hashCode() works in hash tables, such as java.util.HashMap.

(2) If the information used by the object in equals() has not changed, then the hashCode() value remains unchanged.

(3) If two objects are judged to be equal using the equals() method, the hashCode() method should also be equal.

(4) If two objects are judged to be unequal using the equals() method, hashCode() is not required to be unequal; however, developers should realize that unequal objects produce different hashCodes, which can improve the performance of hash tables. performance.

 

3. The role of hashCode()

In general, hashCode() works in hash tables, such as HashSet, HashMap, etc.

When we add an object object to a hash table (such as HashSet, HashMap, etc.), first call the hashCode() method to calculate the hash code of the object, and the position of the object in the hash table can be directly located through the hash code (usually Hash code modulo the size of the hash table). If there is no object in the position, you can directly insert the object into the position; if there are objects in the position (there may be more than one, implemented through a linked list), call the equals() method to compare whether these objects are equal to the object. If they are equal, no need Save the object; if not equal, add the object to the linked list.

This also explains why equals() are equal, then hashCode() must be equal. If two objects equals() are equal, they should only appear once in the hash table (such as HashSet, HashMap, etc.); if hashCode() is not equal, then they will be hashed to different locations in the hash table, ha It appears more than once in the table.

In fact, in the JVM, the loaded object includes three parts in memory: object header, instance data, and padding. Among them, the object header includes a pointer to the type of the object and MarkWord. In addition to the GC generation age information and lock status information of the object, the MarkWord also includes the hashcode of the object; the object instance data is the valid information that the object actually stores. ; The padding part only acts as a placeholder, because HotSpot requires that the starting address of the object must be an integer multiple of 8 bytes.

 

4. Implementation of equals() and hashCode() in String

The relevant implementation code in the String class is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private final char value[];
private int hash; // Default to 0
public boolean equals(Object anObject) {
     if ( this == anObject) {
         return true ;
     }
     if (anObject instanceof String) {
         String anotherString = (String)anObject;
         int n = value.length;
         if (n == anotherString.value.length) {
             char v1[] = value;
             char v2[] = anotherString.value;
             int i = 0 ;
             while (n-- != 0 ) {
                 if (v1[i] != v2[i])
                     return false ;
                 i++;
             }
             return true ;
         }
     }
     return false ;
}
public int hashCode() {
     int h = hash;
     if (h == 0 && value.length > 0 ) {
         char val[] = value;
  
         for ( int i = 0 ; i < value.length; i++) {
             h = 31 * h + val[i];
         }
         hash = h;
     }
     return h;
}

The following points can be seen from the code:

a. The data of String is final, that is, once a String object is created, it cannot be modified; a statement in the form of String s = "hello"; s = "world";, when s = "world" is executed, is not a character The value of the string object becomes "world", but a new String object is created, and the s reference points to the new object.

b. The String class caches the result of hashCode() as a hash value to improve performance.

c. The condition for equals() of String objects to be equal is that they are both String objects, the length is the same, and the string values ​​are exactly the same; they are not required to be the same object.

d. The hashCode() calculation formula of String is: s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]

The main reasons why the number 31 is used in the calculation process of hashCode() are as follows:

a. Use prime numbers to calculate hash codes. Due to the characteristics of prime numbers, after it is multiplied with other numbers, the probability of the calculation result being unique is greater, and the probability of hash collision is smaller.

b. The larger the prime number used, the smaller the probability of hash collision, but the slower the calculation speed; 31 is the compromise between hash collision and performance, which is actually the result of experimental observation.

c. JVM will automatically optimize 31: 31 * i == (i << 5) – i

 

5. How to rewrite hashCode()

This section first introduces the principles that should be followed when rewriting the hashCode() method, and then introduces the general hashCode() rewriting method.

(1) The principle of rewriting hashcode()

From the previous description, we know that rewriting hashCode needs to comply with the following principles:

(1) If the equals() method is overridden, check whether the condition "two objects are judged to be equal using the equals() method, the hashCode() method should also be equal" is true, and if not, rewrite the hashCode() method.

(2) The hashCode() method cannot be too simple, otherwise there will be too many hash collisions.

(3) The hashCode() method cannot be too complicated, otherwise the computational complexity will be too high and the performance will be affected.

(2) hashCode() override method

A. Initialize an integer variable and assign a non-zero constant value to this variable, such as int result = 17;
B. Select all fields used for comparison in the equals method (the reason why only the fields used in equals() are selected is that To guarantee No. 1 of the above principle), then compute for each domain's properties:

  (1) If it is a boolean value, calculate f ? 1:0
  (2) If it is byte\char\short\int, calculate (int)f
  (3) If it is a long value, calculate (int)(f ^ (f >>> 32))
  (4) If it is a float value, calculate Float.floatToIntBits(f)
  (5) If it is a double value, calculate Double.doubleToLongBits(f), and then the returned result is long, and then use Rule (3) to deal with long, get int
  (6) If it is an object application, if the comparison method of recursive call is adopted in the equals method, then the method of recursive call of hashCode is also adopted in hashCode. Otherwise, a normal form needs to be calculated for this field. For example, when the value of this field is null, the hashCode value is 0.
  (7) If it is an array, then each element needs to be treated as a separate field. The java.util.Arrays.hashCode method includes the hashCode calculation of 8 basic types of arrays and reference arrays, and the algorithm is the same as above.

C. Finally, the hash code of each field is merged into the hash code of the object.

 

An example is given below. In this example, the Person class overrides the equals() method and the hashCode() method. Because only the name field and the age field are used in the equals() method, only the name field and the age field are calculated in the hashCode() method.

For the name field of type String, the hashCode() method of String is directly used; for the age field of type int, its value is directly used as the hash of the field.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Person {
     private String name;
     private int age;
     private boolean gender;
  
     public Person() {
         super ();
     }
  
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this .name = name;
     }
     public int getAge() {
         return age;
     }
     public void setAge( int age) {
         this .age = age;
     }
     public boolean isGender() {
         return gender;
     }
     public void setGender( boolean gender) {
         this .gender = gender;
     }
  
     @Override
     public boolean equals(Object another) {
         if ( this == another) {
             return true ;
         }
         if (another instanceof Person) {
             Person anotherPerson = (Person) another;
             if ( this .getName().equals(anotherPerson.getName()) && this .getAge() == anotherPerson.getAge()) {
                 return true ;
             } else {
                 return false ;
             }
         }
         return false ;
     }
  
     @Override
     public int hashCode() {
         int hash = 17 ;
         hash = hash * 31 + getName().hashCode();
         hash = hash * 31 + getAge();
         return hash;
     }
}
 

Guess you like

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