Сравнение объектов Java с помощью equals () и hashcode ()

В этом Java Challenger вы узнаете, как equals()и hashcode()комбинировать, чтобы сделать сравнение объектов эффективным и простым в ваших программах Java. Проще говоря, эти методы работают вместе, чтобы проверить, имеют ли два объекта одинаковые значения.  

Без "" equals()и hashcode()нам пришлось бы создавать очень большие ifсравнения, сравнивая каждое поле объекта. Это сделало бы код действительно запутанным и трудным для чтения. Вместе эти два метода помогают нам создавать более гибкий и целостный код.

Получите исходный код Java Challengers.

Переопределение equals () и hashcode () в Java

Переопределение метода - это метод, при котором поведение родительского класса или интерфейса снова записывается (переопределяется) в подклассе, чтобы воспользоваться преимуществами полиморфизма. Каждый Objectв Java включает в себя equals()и hashcode()метод, но для правильной работы они должны быть переопределены.

Чтобы понять, как работает переопределение с equals()и   hashcode(), мы можем изучить их реализацию в базовых классах Java. Ниже приведен equals()метод в Objectклассе. Метод проверяет, совпадает ли текущий экземпляр с переданным ранее Object.

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

Если hashcode()метод не переопределен, Objectбудет вызван метод по умолчанию в классе. Это собственный метод , что означает, что он будет выполняться на другом языке, таком как C, и вернет некоторый код, относящийся к адресу памяти объекта. (Не так важно точно знать, как работает этот метод, если вы не пишете код JDK.)

 @HotSpotIntrinsicCandidate public native int hashCode(); 

Когда equals()и hashcode()методы не переопределены, вы увидите эти методы вызываются вместо этого. В этом случае методы не выполняют реальную цель equals()и hashcode(), которая заключается в проверке того, имеют ли два или более объекта одинаковые значения.

Как правило, при переопределении equals()необходимо также переопределить hashcode().

Сравнение объектов с помощью equals ()

Мы используем equals()метод для сравнения объектов в Java. Чтобы определить, являются ли два объекта одинаковыми, equals()сравнивает значения атрибутов объектов:

 public class EqualsAndHashCodeExample { public static void main(String... equalsExplanation) { System.out.println(new Simpson("Homer", 35, 120) .equals(new Simpson("Homer",35,120))); System.out.println(new Simpson("Bart", 10, 120) .equals(new Simpson("El Barto", 10, 45))); System.out.println(new Simpson("Lisa", 54, 60) .equals(new Object())); } static class Simpson { private String name; private int age; private int weight; public Simpson(String name, int age, int weight) { this.name = name; this.age = age; this.weight = weight; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Simpson simpson = (Simpson) o; return age == simpson.age && weight == simpson.weight && name.equals(simpson.name); } } } 

При первом сравнении equals()сравнивает текущий экземпляр объекта с переданным объектом. Если два объекта имеют одинаковые значения, equals()вернется true.

Во втором сравнении equals()проверяется, является ли переданный объект нулевым или он типизирован как другой класс. Если это другой класс, то объекты не равны.

Наконец, equals()сравнивает поля объектов. Если два объекта имеют одинаковые значения полей, тогда объекты одинаковы.

Анализ сравнения объектов

Теперь давайте посмотрим на результаты этих сравнений в нашем main()методе. Сначала мы сравниваем два Simpsonобъекта:

 System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120))); 

Объекты здесь идентичны, поэтому результат будет true.

Далее мы Simpsonснова сравниваем два объекта:

 System.out.println(new Simpson("Bart", 10, 45).equals(new Simpson("El Barto", 10, 45))); 

Объекты здесь почти идентичны, но их названия разные: Барт и Эль Барто. Поэтому результат будет false.

Наконец, давайте сравним Simpsonобъект и экземпляр класса Object:

 System.out.println(new Simpson("Lisa", 54, 60).equals(new Object())); 

В этом случае результат будет falseпотому, что типы классов разные.

equals () против ==

На первый взгляд может показаться , что ==оператор и equals()метод делают одно и то же, но на самом деле они работают по-разному. ==Оператор сравнивает ли указать два объектных ссылок на тот же объект. Например:

 System.out.println(homer == homer2); 

В первом сравнении мы создали два разных Simpsonэкземпляра с помощью newоператора. Из-за этого переменные homerи homer2будут указывать на разные Objectссылки в куче памяти. Так что у нас будет falseрезультат.

System.out.println(homer.equals(homer2)); 

Во втором сравнении мы переопределяем equals()метод. В этом случае сравниваются только имена. Поскольку имя обоих Simpsonобъектов - «Гомер», результат будет таким true.

Однозначная идентификация объектов с помощью hashcode ()

Мы используем hashcode()метод для оптимизации производительности при сравнении объектов. Выполнение   hashcode()возвращает уникальный идентификатор для каждого объекта в вашей программе, что значительно упрощает задачу сравнения всего состояния объекта.

Если хэш-код объекта не совпадает с хэш-кодом другого объекта, нет причин для выполнения equals()метода: вы просто знаете, что эти два объекта не совпадают. С другой стороны, если хэш - код это то же самое, то вы должны выполнить equals()метод , чтобы определить , являются ли значения и поля являются одинаковыми.

Вот практический пример с hashcode().

 public class HashcodeConcept { public static void main(String... hashcodeExample) { Simpson homer = new Simpson(1, "Homer"); Simpson bart = new Simpson(2, "Homer"); boolean isHashcodeEquals = homer.hashCode() == bart.hashCode(); if (isHashcodeEquals) { System.out.println("Should compare with equals method too."); } else { System.out.println("Should not compare with equals method because " + "the id is different, that means the objects are not equals for sure."); } } static class Simpson { int id; String name; public Simpson(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o)  if (this == o) return true; if (o == null  @Override public int hashCode() { return id; } } } 

A, hashcode()который всегда возвращает одно и то же значение, действителен, но не очень эффективен. В этом случае сравнение всегда будет возвращаться true, поэтому equals()метод всегда будет выполняться. В этом случае улучшения производительности нет.  

Использование equals () и hashcode () с коллекциями

SetИнтерфейс отвечает за обеспечение не будут вставлены в не повторяющихся элементов Setподкласса. Ниже приведены некоторые классы, реализующие Setинтерфейс:

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

Только уникальные элементы могут быть вставлены в Set, так что если вы хотите добавить элемент к HashSetклассу (например), вы должны сначала использовать equals()и hashcode()методы , чтобы убедиться , что элемент является уникальным. Если equals()и hashcode()методы не были переопределены в этом случае вы рискуете вставки повторяющихся элементов в коде.

В приведенном ниже коде мы используем этот addметод для добавления нового элемента к HashSetобъекту. Перед добавлением нового элемента HashSetпроверяется, существует ли уже элемент в данной коллекции:

 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; 

Если объект такой же, новый элемент не будет вставлен.

Коллекции хэшей

Setне единственная коллекция, в которой используются equals()и hashcode(). Эти методы также требуются для HashMap, Hashtable и LinkedHashMap. Как правило, если вы видите коллекцию , которая имеет префикс «Hash» , вы можете быть уверены в том , что она требует переопределения hashcode()и equals()методы , чтобы сделать их функции работают правильно.  

Рекомендации по использованию equals () и hashcode ()

Вы должны выполнять equals()метод только для объектов, имеющих одинаковый уникальный идентификатор хэш-кода. Вы не должны выполнять, equals()если ID хэш-кода отличается.

Таблица 1. Сравнение хэш-кодов

Если hashcode()сравнение ... Потом …
возвращает истину выполнять equals()
возвращает ложь не казнить equals()

This principle is mainly used in Set or Hash collections for performance reasons.

Rules for object comparison

When a hashcode() comparison returns false, the equals() method must also return false. If the hashcode is different, then the objects are definitely not equal.

Table 2. Object comparison with hashcode()

When the hashcode comparison returns ... The equals() method should return ...
true true or false
false false

When the equals() method returns true, it means that the objects are equal in all values and attributes. In this case,  the hashcode comparison must be true as well.

Table 3. Object comparison with equals()

When the equals() method returns ... The hashcode() method should return ...
true true
false true or false

Take the equals() and hashcode() challenge!

It’s time to test your skills with the equals() and hashcode() methods.  Your goal in this challenge is to figure out the output of the two equals() method comparisons and guess the size of the Set collection.

To start, study the following code carefully:

 public class EqualsHashCodeChallenge { public static void main(String... doYourBest) { System.out.println(new Simpson("Bart").equals(new Simpson("Bart"))); Simpson overriddenHomer = new Simpson("Homer") { public int hashCode() { return (43 + 777) + 1; } }; System.out.println(new Simpson("Homer").equals(overriddenHomer)); Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge"))); set.add(new Simpson("Homer")); set.add(overriddenHomer); System.out.println(set.size()); } static class Simpson { String name; Simpson(String name) { this.name = name; } @Override public boolean equals(Object obj) { Simpson otherSimpson = (Simpson) obj; return this.name.equals(otherSimpson.name) && this.hashCode() == otherSimpson.hashCode(); } @Override public int hashCode() { return (43 + 777); } } } 

Remember, analyze the code first, guess the result, and then run the code. Your goal is to improve your skill with code analysis and absorb core Java concepts to make your code more powerful. Choose your answer before checking the correct answer below.

 A) true true 4 B) true false 3 C) true false 2 D) false true 3 

What just happened? Understanding equals() and hashcode()

In the first equals() method comparison, the result is true because the state of the object is exactly the same and the hashcode() method returns the same value for both objects.

In the second equals() method comparison, the hashcode() method is being overridden for the overridenHomer variable. The name is “Homer” for both Simpson objects, but the hashcode() method returns a different value for overriddenHomer. In this case, the final result from the the equals() method will be false because the method contains a comparison with the hashcode.

You might notice that the size of the collection is set to hold three Simpson objects. Let’s check this in a detailed way.

The first object in the set will be will be inserted normally:

 new Simpson("Homer"); 

The next object will be inserted normally, as well, because it holds a different value from the previous object:

 new Simpson("Marge"); 

Finally,  the following Simpson object has the same value as the first object. In this case the object won’t be inserted:

 set.add(new Simpson("Homer")); 

Как мы знаем, overridenHomerобъект использует значение хэш-кода, отличное от обычного Simpson(“Homer”)экземпляра. По этой причине этот элемент будет вставлен в коллекцию:

 overriddenHomer; 

Ключ ответа

Ответ на этот Java претендентом является B . Результатом будет:

 true false 3 

Видео вызов! Отладка equals () и hashcode ()

Отладка - один из самых простых способов полностью усвоить концепции программирования, а также улучшить код. В этом видео вы можете следить за тем, как я отлаживаю, и объяснять Java equals()и hashcode()проблему.