Hashcode and equals override in java

Java hashCode() and equals() Methods

Learn about Java hashCode() and equals() methods, their default implementation, and how to correctly override them. Also, we will learn to implement these methods using 3rd party classes HashCodeBuilder and EqualsBuilder .

The hashCode() and equals() methods have been defined in Object class which is parent class for all java classes. For this reason, all java objects inherit a default implementation of these methods.

1. The hashCode() and equals() Methods

  • equals(Object otherObject) – verifies the equality of two objects. It’s default implementation simply checks the object references of two objects to verify their equality. By default, two objects are equal if and only if they refer to the same memory location. Most Java classes override this method to provide their own comparison logic.
  • hashcode() – returns a unique integer value for the object in runtime. By default, Integer value is derived from the memory address of the object in the heap (but it’s not mandatory). The object’s hash code is used for determining the index location when this object needs to be stored in some HashTable like data structure.

1.1. Contract between hashCode() and equals()

Overriding the the hashCode() is generally necessary whenever equals() is overridden to maintain the general contract for the hashCode() method, which states that equal objects must have equal hash codes.

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode () must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
    This integer need not remain consistent between the two executions of the same application or program.
  • If two objects are equal according to the equals() method, then calling the hashCode() on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals() , then calling the hashCode() on each of the both objects must produce distinct integer results.
    However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

2. Overriding the Default Behavior

Everything works fine until we do not override any of both methods in our classes. But, sometimes, the application needs to change the default behavior of some objects.

Let us understand why we need to override equals and hashcode methods.

Let’s take an example where your application has Employee object. Let us create a minimal possible structure of Employee class:

Above Employee class has some fundamental attributes and their accessor methods. Now consider a simple situation where you need to compare two Employee objects. Both employee objects have the same id .

No prize for guessing. The above method will print “false.”

But is it correct after knowing that both objects represent the same employee? In a real-time application, this should return true .

2.2. Overriding only equals()

Читайте также:  Основные операции над множествами python

To achieve correct application behavior, we need to override equals() method as below:

public boolean equals(Object o) < if(o == null) < return false; >if (o == this) < return true; >if (getClass() != o.getClass()) < return false; >Employee e = (Employee) o; return (this.getId() == e.getId()); >

Add this method to the Employee class, and EqualsTest will start returning «true» .

So are we done? Not yet. Let’s test the above-modified Employee class again in a different way.

import java.util.HashSet; import java.util.Set; public class EqualsTest < public static void main(String[] args) < Employee e1 = new Employee(); Employee e2 = new Employee(); e1.setId(100); e2.setId(100); //Prints 'true' System.out.println(e1.equals(e2)); Setemployees = new HashSet(); employees.add(e1); employees.add(e2); System.out.println(employees); //Prints two objects > >

The above example prints two objects in the second print statement.

If both employee objects have been equal, in a Set which stores unique objects, there must be only one instance inside HashSet because both objects refer to the same employee. What is it we are missing??

2.3. Overriding hashCode() is Also Necessary

We are missing the second important method hashCode() . As java docs say, if we override equals() then we must override hashCode() . So let’s add another method in our Employee class.

@Override public int hashCode()

Once the above method is added in Employee class, the second statement starts printing only a single object in the second statement and thus validating the true equality of e1 and e2 .

3. EqualsBuilder and HashCodeBuilder

Apache commons provide two excellent utility classes HashCodeBuilder and EqualsBuilder for generating hash code and equals methods.

We can use these classes in the following manner.

import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; public class Employee < private Integer id; private String firstname; private String lastName; private String department; //Setters and Getters @Override public int hashCode() < final int PRIME = 31; return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).toHashCode(); >@Override public boolean equals(Object o) < if (o == null) return false; if (o == this) return true; if (o.getClass() != getClass()) return false; Employee e = (Employee) o; return new EqualsBuilder(). append(getId(), e.getId()). isEquals(); >>

4. Generating hashCode() and equals() in Eclipse IDE

Most editors provide common source code templates. For example, Eclipse IDE has the option to generate an excellent implementation of hashCode() and equals() .

Right click on Java file -> Source -> Generate hashCode() and equals() …

Generate HashCode and Equals In Eclipse

5. Best Practices to Follow

  1. Always use the same fields to generate hashCode() and equals() . As in our case, we have used employee id .
  2. The equals() must be consistent (if the objects are not modified, then it must keep returning the same value).
  3. Whenever a.equals(b), then a.hashCode() must be same as b.hashCode().
  4. If we override one method, then we should override the other method as well.

6. Special Attention When Declared in an JPA Entity

If you’re dealing with an ORM, make sure always to use getters and never use the field references in hashCode() and equals() . Because in ORM, occasionally fields are lazy loaded and not available until we call their getter methods.

For example, In our Employee class if we use e1.id == e2.id . It is very much possible that id field is lazy-loaded. So, in this case, id field inside the methods might be zero or null , and thus resulting in incorrect behavior.

Читайте также:  Html программа без программирования

But if uses e1.getId() == e2.getId() , we can be sure even if the field is lazy-loaded, calling the field getter will populate the field first.

Источник

Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java?

Java-университет

Зачем переопределять методы equals и hashcode в Java?

Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java? - 1

Источник: Medium Содержание этой статьи посвящено двум тесно связанным между собой методам: equals() и hashcode() . Вы узнаете, как они взаимодействуют друг с другом и как их правильно переопределять.

Почему мы переопределяем метод equals()?

В Java мы не можем перегружать поведение таких операторов, как == , += , -+ . Они работают согласно заданному процессу. Для примера рассмотрим работу оператора == .

Как работает оператор ==?

Он проверяет, указывают ли две сравниваемые ссылки на один и тот же экземпляр в памяти. Оператор == будет иметь значение true только в том случае, если эти две ссылки представляют один и тот же экземпляр в памяти. Давайте взглянем на пример кода:

 Person person1 = new Person("Mike", 34); Person person2 = new Person("Mike", 34); System.out.println( person1 == person2 ); --> will print false! 

С точки зрения бизнеса эти два объекта выглядят одинаково, верно? Но для JVM они не совпадают. Поскольку они оба созданы с помощью ключевого слова new , эти экземпляры расположены в разных сегментах памяти. Поэтому оператор == вернет false. Но если мы не можем переопределить оператор == , то как нам сказать JVM, что мы хотим, чтобы эти два объекта обрабатывались одинаково? Здесь в игру вступает метод .equals() . Вы можете переопределить equals() , чтобы проверить, имеют ли некоторые объекты одинаковые значения для определенных полей, чтобы считать их равными. Вы можете выбрать, какие поля нужно сравнить. Если мы говорим, что два объекта Person будут одинаковыми только тогда, когда они имеют одинаковый возраст и одно и то же имя, то в этом случае IDE сгенерирует для автоматического создания equals() что-то такое:

 @Override public boolean equals(Object o)
 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! 

Да, мы не можем перегрузить оператор == для сравнения объектов так, как мы хотим, но Java дает нам другой способ — метод equals() , который мы можем переопределить по своему усмотрению. Имейте в виду, что если мы не предоставим нашу пользовательскую версию .equals() (также известную как переопределение) в нашем классе, то предопределенный .equals() из класса Object и оператор == будут вести себя одинаково. Метод по умолчанию equals() , унаследованный от Object , будет проверять, совпадают ли оба сравниваемых экземпляра в памяти!

Почему мы переопределяем метод hashCode()?

Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java? - 2

Некоторые структуры данных в Java, такие как HashSet и HashMap , хранят свои элементы на основе хеш-функции, которая применяется к этим элементам. Хеш-функцией является hashCode() . Если у нас есть выбор в переопределении метода .equals() , то у нас также должен быть выбор в переопределении метода hashCode() . Для этого есть причина. Ведь реализация по умолчанию hashCode() , унаследованная от Object , считает все объекты в памяти уникальными! Но вернемся к этим структурам хеш-данных. Для этих структур данных существует правило. HashSet не может содержать повторяющиеся значения, а HashMap не может содержать повторяющиеся ключи. HashSet реализован с помощью HashMap таким образом, что каждое значение HashSet хранится как ключ в HashMap . Как работает HashMap ? HashMap — это собственный массив с несколькими сегментами. Каждый сегмент имеет связанный список ( linkedList ). В этом связанном списке хранятся наши ключи. HashMap находит правильный linkedList для каждого ключа, применяя метод hashCode() , а затем выполняет итерацию по всем элементам этого linkedList и применяет метод equals() к каждому из этих элементов, чтобы проверить, содержится ли там этот элемент. Дубликаты ключей не допускаются. Когда мы помещаем что-то внутрь HashMap , то ключ сохраняется в одном из этих связанных списков. В каком связанном списке будет храниться этот ключ, показывает результат метода hashCode() для этого ключа. То есть, если key1.hashCode() в результате получается 4, то этот key1 будет храниться в 4-м сегменте массива в существующем там LinkedList . По умолчанию метод hashCode() возвращает разные результаты для каждого экземпляра. Если у нас есть значение по умолчанию equals() , которое ведет себя как == , рассматривая все экземпляры в памяти как разные объекты, то проблем не будет. Как вы помните, в нашем предыдущем примере было сказано, что мы хотим, чтобы экземпляры Person считались равными, если их возраст и имена совпадают.

 Person person1 = new Person("Mike", 34); Person person2 = new Person("Mike", 34); System.out.println ( person1.equals(person2) ); --> will print true! 

Теперь давайте создадим карту (map) для хранения этих экземпляров в виде ключей с определенной строкой в ​​качестве парного значения.

 Map map = new HashMap(); map.put(person1, "1"); map.put(person2, "2"); 

Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java? - 3

В классе Person мы не переопределили метод hashCode , но у нас есть переопределенный метод equals . Поскольку значение по умолчанию hashCode дает разные результаты для разных Java-экземпляров person1.hashCode() и person2.hashCode() , есть большие шансы получить разные результаты. Наша карта может заканчиваться разными person в разных связанных списках. Это противоречит логике HashMap . Ведь HashMap не может иметь несколько одинаковых ключей! Дело в том, что по умолчанию hashCode() , унаследованного от класса Object , недостаточно. Даже после того, как мы переопределили метод equals() класса Person . Вот почему мы должны переопределить метод hashCode() после того, как мы переопределили метод equals . Теперь давайте это исправим. Нам нужно переопределить наш метод hashCode() , чтобы он учитывал те же поля, что и equals() , а именно age и 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()

В методе hashCode() мы использовали простое значение (вы можете использовать любые другие значения). Тем не менее, предлагается использовать простые числа, чтобы создавать меньше проблем. Давайте попробуем еще раз сохранить эти ключи в нашем HashMap :

 Map map = new HashMap(); map.put(person1, "1"); map.put(person2, "2"); 

Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java? - 4

person1.hashCode() и person2.hashCode() будут одинаковы. Допустим, равны 0. HashMap перейдет в сегмент 0 и в нем LinkedList сохранит person1 как ключ со значением “1”. Во втором случае, когда HashMap снова перейдет к корзине 0, чтобы сохранить ключ person2 со значением “2”, он увидит, что там уже существует другой равный ему ключ. Таким образом он перезапишет предыдущий ключ. И в нашем HashMap будет существовать только ключ person2 . Так мы узнали, как работает правило HashMap , которое гласит, что нельзя использовать несколько одинаковых ключей! Однако имейте в виду, что неравные экземпляры могут иметь одинаковый хэшкод, а одинаковые экземпляры должны возвращать одинаковый хэшкод.

Источник

Оцените статью