preface

In Effective Java, article 9 states that hashCode() should always be overridden when overriding equals(). Why is that? Let’s take a look at these two methods.

The Equals () and hashCode() methods in Java are methods in the Object class, and all classes in Java are subclasses of the Obejct class, so all methods in Java have default implementations of these two methods.

The equals method

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

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

We found the key == in equals(), so what does == mean in Java?

We all know that there are primitive data types and reference data types in Java. So == works differently in these two types.

  • Basic data types: Compare whether the values of == are equal
  • Reference datatype: compares whether the memory address on both sides of == is equal

Basic data types include: byte, short, char, int, long, float, double, Boolean

According to the Equals () method described in the Java documentation, all who want to implement their equals() method must follow the following rules

  • reflexivityFor any object x, x.equals(x) should return true
  • symmetry: For any two objects x and y, if x.quals (y) returns true, then y.quals (x) should also return true
  • transitivity: For multiple objects x, y, and z, if x.quals (y) returns true and y.quals (z) returns true, then y.quals (z) should also return true
  • consistency: For two non-empty objects x and y, multiple calls should return the same result without modifying the object
  • For any non-empty object x, x.equals(null) should return false

HashCode methods

The hashCode() method in Object is a native method that returns a hash of type int.

public native int hashCode();
Copy the code

There are also some conventions in the hashCode() method

  • If the parameters of an object are not modified using equals, the hashCode() method of an object should return the same hash value multiple times.
  • If two objects are equal by comparing them using the equals method, then the value returned by the hashCode method of the two objects should also be equal.
  • There is no requirement that the hashCode method of two objects return the same value if they are different by comparing them using equals. But we should know that producing different hash values for different objects can improve performance for hash tables (hashmaps, etc.).

Where are the equals and hashCode methods used

Which class in Java do these two methods appear most often? Those of you who have read the HashMap source code should know that these two methods appear frequently in HashMap.

There are many articles on the web that introduce the HashMap class. Here is a brief introduction to HashMap.

When the list in a node exceeds 8, it will become a red-black tree to solve the problem of slow query speed when the list is too long.

A HashMap is an efficient data storage structure composed of arrays and linked lists. So how do you figure out where a data is stored in an array?

It’s just using the hashCode method to figure out where it’s stored, and remember we talked about the hashCode method above where it’s possible for two different objects to return the same value.

If they are different, they are added to the end of the list. If they are the same, they are replaced.

Of course, it’s not as simple as a hashCode method above. There are other steps in between, and we can simply say that hashCode determines the position.

When do you override these two methods?

If you do not define a custom class as a HashMap key, then we override equals instead of hashCode, and the compiler does not raise any errors and does not throw any exceptions at runtime.

If you want to define a custom class as the key value of a HashMap, then overriding equals must also override the hashCode method.

Next we can see what happens if we use a custom class as the key of a HashMap and the custom class does not override equals and hashCode methods.

@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomizedKey{
    private Integer id;
    private String name;
}
Copy the code
public static void main(String[] args) {

    Map<CustomizedKey, Integer> data = getData();

    CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();

    Integer integer = data.get(key);

    System.out.printf(String.valueOf(integer));
}

private static Map<CustomizedKey,Integer> getData(){
    Map<CustomizedKey,Integer> customizedKeyIntegerMap = new HashMap<>();
    CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
    customizedKeyIntegerMap.put(key,10);
    return customizedKeyIntegerMap;
}
Copy the code

We can see that the program finally prints a null value. The reason is as we said above.

HashCode: Calculates where the object should be placed in the array. Since both objects are new, the values inside are the same, but the address of the object is different, so the default hashCode is different. Of course, in a hashMap, two objects are not considered one object.

So let’s rewrite these two methods. If we use IDEA, we can just use the shortcut keys.

Let’s look at the two methods we implement

@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomizedKey{
    private Integer id;
    private String name;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if(o == null || getClass() ! = o.getClass())return false;
        CustomizedKey that = (CustomizedKey) o;
        return Objects.equals(id, that.id) &&
                Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        returnObjects.hash(id, name); }}Copy the code

Then we run the program again and find that the output print has changed to 10.

From: jianshu.com/p/53e233e8abad