JavaRush /Java Blog /Random EN /Coffee break #168. Why override equals and hashcode metho...

Coffee break #168. Why override equals and hashcode methods in Java?

Published in the Random EN group

Why override equals and hashcode methods in Java?

Source: Medium This article focuses on two closely related methods: equals() and hashcode() . You'll learn how they interact with each other and how to override them correctly. Coffee break #168.  Why override equals and hashcode methods in Java?  - 1

Why do we override the equals() method?

In Java, we cannot overload the behavior of operators like == , += , -+ . They work according to a given process. For example, consider the operation of the == operator .

How does the == operator work?

It checks whether the two references being compared point to the same instance in memory. The == operator will only evaluate to true if the two references represent the same instance in memory. Let's take a look at the sample code:
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
Let's say in your program you have created two Person objects in different places and want to compare them.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
From a business perspective, the two look the same, right? But for the JVM they are not the same. Since they are both created using the new keyword , these instances are located in different memory segments. Therefore the == operator will return false . But if we can't override the == operator , then how do we tell the JVM that we want these two objects to be treated the same? This is where the .equals() method comes into play . You can override equals() to check if some objects have the same values ​​for certain fields in order to consider them equal. You can choose which fields to compare. If we say that two Person objects will be the same only if they have the same age and the same name, then the IDE will generate something like this to automatically create equals() :
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
Let's return to our previous example.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
Yes, we cannot overload the == operator to compare objects the way we want, but Java gives us another way - the equals() method , which we can override as we wish. Keep in mind that if we don't provide our custom version of .equals() (also known as an override) in our class, then the predefined .equals() from the Object class and the == operator will behave the same. The default equals() method , inherited from Object , will check if both instances being compared are the same in memory!

Why are we overriding the hashCode() method?

Some data structures in Java, such as HashSet and HashMap , store their elements based on a hash function that is applied to those elements. The hash function is hashCode() . If we have a choice in overriding the .equals() method , then we should also have a choice in overriding the hashCode() method . There's a reason for this. After all, the default implementation of hashCode() , inherited from Object , considers all objects in memory to be unique! But let's get back to these hash data structures. There is a rule for these data structures. A HashSet cannot contain duplicate values ​​and a HashMap cannot contain duplicate keys. A HashSet is implemented using a HashMap in such a way that each HashSet value is stored as a key in the HashMap . How does HashMap work ? HashMap is a native array with multiple segments. Each segment has a linked list ( linkedList ). This linked list stores our keys. HashMap finds the correct linkedList for each key using the hashCode() method , and then iterates through all the elements of that linkedList and applies the equals() method to each of those elements to check if that element is contained there. Duplicate keys are not allowed. Coffee break #168.  Why override equals and hashcode methods in Java?  - 2When we put something inside a HashMap , the key is stored in one of these linked lists. Which linked list this key will be stored in is shown by the result of the hashCode() method for that key. That is, if key1.hashCode() results in 4, then that key1 will be stored in the 4th segment of the array in the LinkedList existing there . By default, the hashCode() method returns different results for each instance. If we have a default equals() that behaves like == , treating all instances in memory as different objects, then there won't be a problem. As you may recall, in our previous example we said that we want Person instances to be considered equal if their ages and names are the same.
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
Now let's create a map to store these instances as keys with a specific string as the value pair.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
In the Person class, we have not overridden the hashCode method , but we have an overridden equals method . Since the default hashCode gives different results for different Java instances of person1.hashCode() and person2.hashCode() , there are high chances of getting different results. Our map can end with different persons in different linked lists. Coffee break #168.  Why override equals and hashcode methods in Java?  - 3This goes against the logic of HashMap . After all, a HashMap cannot have several identical keys! The point is that the default hashCode() inherited from the Object class is not enough. Even after we override the equals() method of the Person class . That's why we have to override hashCode() method after we override equals method . Now let's fix this. We need to override our hashCode() method so that it takes into account the same fields as equals() , namely age and name .
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
In the hashCode() method we used a simple value (you can use any other values). However, it is suggested to use prime numbers to create fewer problems. Let's try storing these keys in our HashMap again :
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode() and person2.hashCode() will be the same. Let's say they are 0. The HashMap will go to segment 0 and in it the LinkedList will save person1 as a key with the value “1”. In the second case, when HashMap goes to bucket 0 again to store the key person2 with the value “2”, it will see that another key equal to it already exists there. This way it will overwrite the previous key. And only the key person2 will exist in our HashMap . This is how we learned how the HashMap rule works , which states that you cannot use multiple identical keys! However, keep in mind that unequal instances can have the same hashcode, and equal instances must return the same hashcode.Coffee break #168.  Why override equals and hashcode methods in Java?  - 4
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION