Figure out equals and hashCode at once

Foreword
In programming, there are many "conventions". Following the conventions to implement your code will allow you to avoid many pitfalls. These conventions are the design specifications summed up by predecessors.

The Object class is the ancestor of all classes in Java. Among them, equals and hashCode are two very important methods.

These two methods are always discussed together. Recently, I was looking at the collection framework. In order to lay the foundation, I decided to clean up some details. Find out at once!

Let's start the analysis.

public boolean equals(Object obj)

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 certain runtime, the change of the state of 2 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.

one example

class Test{
    private int num;
    private String data;

   public boolean equals(Object obj) {
         if (this == obj)
             return true; 
         if ((obj == null) || (obj.getClass() != this.getClass()))
  return false;

           //能执行到这里,说明obj和this同类且非null。
       Test test = (Test) obj;
    return num == test.num&& (data == test.data || (data != null && data.equals(test.data)));
  }

    public int hashCode()
   {
         //重写equals,也必须重写hashCode。具体后面介绍。
   }

}

equals writing guide

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.

In line 8, the reference of the incoming comparison object is compared with this. This is to save time and save execution time. If this and obj are references to the same heap object, then they must be qeuals.

Next, judge whether obj is null. If it is null, it must not be equals, because since the current object this can call the equals method, it must not be null, and non-null and null are of course not equivalent.

Then, compare the runtime classes of the 2 objects to see if they are the same class. not the same class, do not equals. getClass returns a reference to the runtime class of this and obj. If they belong to the same class, a reference to the same runtime class is returned. Note that a class is also an object.

1. Some programmers use the second way of writing below to replace the first way of comparing runtime classes. This should be avoided.

if((obj == null) || (obj.getClass() != this.getClass())) 

     return false; 


if(!(obj instanceof Test)) 

     return false; // avoid 避免!

It violates the symmetry principle in the convention.

For example: Suppose Dog extends the Aminal class.

dog instanceof Animal gets true

animal instanceof Dog gets false

This will lead to

animal.equls(dog) returns true
dog.equals(animal) returns false

This is only guaranteed to be correct if the Test class has no subclasses.

2. Implemented according to the first method, then equals can only compare objects of the same class, and objects of different classes are always false. But this is not mandatory. In general, we rarely need to use equals between different classes.

3. When comparing the fields of objects, for fields of basic value type, use == to compare directly (note the comparison of floating-point numbers, this is a pit) For fields of reference type, you can call their equals, of course , you also need to handle the case where the field is null. For the comparison of floating-point numbers, when I looked at the source code of Arrays.binarySearch, I found the following tricks for comparing floating-point numbers:

if ( Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2) ) //d1 和 d2 是double类型

if( Float.floatToIntBits(f1) == Float.floatToIntBits(f2) ) //f1 and f2 are d2 is float type

4. It is not always necessary to use all fields of the object as the basis for evaluating equals, it depends on your business requirements. For example, if you want to make a home appliance power statistics system, if the power of two home appliances is the same, then there is enough basis to think that these two home appliance objects are equivalent, at least in the context of your business logic. Their price, brand, size and other parameters.

5. The last thing to note is that the parameter type of the equals method is Object, don't make a mistake!

public int hashCode()

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
that objects that override the euqls method must also override the hashCode() method.

If two objects return true after being called by equals, then the hashCode method of the two objects must also return the same int hash code

If 2 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.)

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.

Makes sense: different objects in the same class return different hash codes. The typical way is to convert the hash code of the object according to the address of the object, but this way is not
the . Usually not the best way to do it either.

Compared with the recognized implementation convention of equals, the convention requirements of hashCode are easy to understand. There are 2 important points that the hashCode method must comply with. The third point of the agreement is actually the
refinement . Let's take a look at the consistent agreement requirements for the hashCode method.

First: During a runtime period, as long as the object's (field) changes do not affect the decision result of the equals method, then, during this period, no matter how many times hashCode is called, the same hash code must be returned.

Second: The hashCode of the 2 objects that return true through the equals call must be the same.

Third: The hash codes of the two objects that return false through equalsl do not need to be different, that is, the return value of their hashCode method allows the same situation.

To sum up in one sentence: equivalent (calling equals returns true) objects must produce the same hash code. Non-equivalent objects do not require the generated hash codes to be different.

hashCode writing guide

When writing hashCode, you need to consider that the final hash is an int value and cannot overflow. The hash codes of different objects should be as different as possible to avoid hash conflicts.

So what if it does? Below is the solution.

1. Define a variable hash of type int and initialize it to 7.

Next, let the fields you think are important (fields that measure equality in equals) participate in the hash operation, and each important field will generate a hash component to contribute (influence) to the final hash value.

Operation method reference table Types of
important fields var hash components
byte, char, short , int (int)var
long (int)(var ^ (var >>> 32))
boolean var?1:0
float Float.floatToIntBits (var)
double long bits = Double.doubleToLongBits(var);
component = (int)(bits ^ (bits >>> 32));
reference type(null == var ? 0 : var.hashCode())

Finally add all the components together, note that it is not a simple addition. Choose a multiplied number 31 to participate in the calculation. Then keep recursively calculating until all fields are involved.

int hash = 7;

hash = 31 * hash + 字段1贡献分量;

hash = 31 * hash + 字段2贡献分量;

.....

return hash

Guess you like

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