Как переопределить equals java

Какова связь между equals() и hashcode() ? Узнайте, как эти методы работают вместе при сравнении объектов в Java.

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

Без equals() и hashcode() нам пришлось бы создавать громоздкие сравнения «if», сопоставляя каждое поле с объектом. Что сделало бы исходный код запутанным.

Скачать исходный код .

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

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

Ниже приведен метод equals() в классе Object. Метод проверяет, совпадает ли текущий экземпляр с ранее переданным объектом.

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

Если equals() и hashcode() не переопределены, вместо них вы увидите приведенные выше методы. В этом случае методы не выполняют задачу equals() и hashcode() — проверку, имеют ли два (или более) объекта одинаковые значения.

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

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

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

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

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

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

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

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

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

Объекты почти идентичны, но поля name имеют разные значения (Bart и El Barto). Поэтому результат будет false .

Сравним объект Simpson и экземпляр класса Object :

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

equals () или ==

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

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

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

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

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

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

Читайте также:  Как подобрать пароль к аккаунту

Практический пример использования hashcode() .

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

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

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

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

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

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

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

Хэш-коллекции

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

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

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

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

Если сравнение hashcode() … Тогда …
возвращает true выполнить equals ()
возвращает false не выполнять equals ()

Этот принцип используется в коллекциях Set или Hash для повышения производительности.

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

Когда сравнение hashcode() возвращает false , метод equals() также должен возвращать значение false . Если хэш-код отличается, то объекты не равны.

Таблица 2. Сравнение объектов с помощью hashcode()

Когда сравнение хэш-кодов возвращает… метод equals() должен возвращать …
True true или false
False False

Когда метод equals() возвращает true , то объекты равны во всех значениях и атрибутах. В этом случае сравнение хэш-кодов должно возвращать true.

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

Когда метод equals() возвращает … метод hashcode() должен возвращать…
True True
False true или false

Выполните задание на использование equals() и hashcode()!

Пришло время проверить свои навыки. Нужно получить результат двух сравнений метода equals() и узнать размер коллекции Set .

Для начала внимательно изучите приведенный ниже код:

Проанализируйте код, угадайте результат, а затем запустите программу. Ваша цель заключается в том, чтобы улучшить навыки анализа кода,усвоить основные концепции Java и сделать создаваемый код более эффективным. Выберите свой вариант, прежде чем проверять правильный ответ, который приведен ниже.

Что сейчас произошло? Понимание equals() и hashcode()

В первом сравнении equals() результат верен, потому что метод hashcode() возвращает одно и то же значение для обоих объектов.

Во втором случае метод hashcode() переопределяется для переменной overr >hashcode() возвращает другое значение для overr >equals() будет false , поскольку метод содержит сравнение с хэш-кодом.

Обратите внимание, что размер коллекции задан для хранения трех объектов Simpson. Рассмотрим это более подробно.

Первый объект в наборе будет добавлен в коллекцию:

Следующий объект также будет добавлен, поскольку имеет отличное от предыдущего объекта значение:

Последний объект Simpson имеет то же значение, что и первый. В этом случае объект не будет вставлен:

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

Ответ

Распространенные ошибки использования equals() и hashcode()

  • Отсутствие переопределения hashcode() вместе с методом equals() или наоборот.
  • Не переопределенные equals() и hashcode() при использовании хеш-коллекций, таких как HashSet.
  • Возврат константного значения в методе hashcode() вместо уникального кода для каждого объекта.
  • Использование == и equals взаимозаменяемо. == с равнивает ссылки на объекты, тогда как equals() сравнивает значения объектов.
Читайте также:  Как вставить текст через клавиатуру

Что нужно помнить о equals() и hashcode()

  • Рекомендованная практика — всегда переопределять методы equals() и hashcode() в POJO .
  • Используйте эффективный алгоритм для генерации уникального хэш-кода.
  • При переопределении метода equals() всегда переопределяйте hashcode() .
  • Метод equals() должен сравнивать все значения полей.
  • Метод hashcode() может быть идентификатором POJO.
  • Если equals() и hashcode() не переопределяются при использовании хэш-коллекций, коллекция будет иметь повторяющиеся элементы.

Данная публикация представляет собой перевод статьи « Java Challengers #4: Comparing Java objects with equals() and hashcode() » , подготовленной дружной командой проекта Интернет-технологии.ру

Рассмотрим следующую Java-программу:

ссылка на сайт
brightness_4
код

Причина вывода «Не равно» проста: когда мы сравниваем c1 и c2, проверяется, ссылаются ли оба c1 и c2 на один и тот же объект или нет ( переменные объекта всегда являются ссылками в Java ). c1 и c2 относятся к двум разным объектам, поэтому значение (c1 == c2) равно false. Если мы создадим еще одну ссылку, скажем, c3, как показано ниже, то (c1 == c3) даст true.

ссылка на сайт
brightness_4
код

Итак, как мы проверяем равенство значений внутри объектов? Все классы в Java наследуются от класса Object прямо или косвенно (см. Пункт 1 этого ). Класс Object имеет несколько основных методов, таких как clone (), toString (), equals () и т. Д. Мы можем переопределить метод equals в нашем классе, чтобы проверить, имеют ли два объекта одинаковые данные или нет.

ссылка на сайт
brightness_4
код

В качестве примечания, когда мы переопределяем equals (), рекомендуется также переопределить метод hashCode (). Если мы этого не сделаем, равные объекты могут получить разные хеш-значения; и коллекции на основе хешей, включая HashMap, HashSet и Hashtable, не работают должным образом (см. это для более подробной информации). Подробнее о hashCode () мы расскажем в отдельном посте.

Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.

Для начала. Объекты в java могут быть между собой равны, а могут быть эквивалентны

1) равными(через знак ==) могут быть только ссылки, не объекты.
2) эквивалентными называются объекты с одинаковыми значениями полей
но equals != эквивалентность
объясним почему:

hashcode()

hashcode() – метод Object, возвращающий int значение.

В классе Object метод hashCode() определен как public native int hashCode();
исходный код hashCode() написан на другом языке.
Алгоритм генерации хешкода для объекта работает с учаcтием генератора случайных чисел, то есть при каждом выполнении программы хеш-коды объектов будут разными

Однако следует пристально обратить внимание на одно разительное несовершенство функции hashCode():
Если хешкоды объектов не равны, то объекты гарантированно разные. Но, неверно обратное,- если хешкоды объектов равны, то и сами объекты равны.
Ситуация, когда у разных объектов появляются одинаковые хешкоды называется коллизией.
A – хешкод x равен хешкоду y
B – x = y
1) -A -> -B если хешкоды объектов неравны, то объекты неравны
2) -(A -> B) неверно, что если хешкоды равны, то объекты равны
таким образом
(-A -> -B) & (-(A -> B))
(-A / B) & (-(A / -B))
(-A / B) & (-A / B)
получаем две истины
-A / B
1) хешкоды неравны, и объекты неравны
2) хешкоды равны, объекты неравны
3) хешкоды равны, объекты равны

-A / B и есть ситуация коллизии, хешкоды неравны, но объекты равны

equals()

В идеале етод equals() класса Object должен проверять объекты на эквивалентность, исходная же реализация метода equals в классе Object

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

Резюмируем:
Итак мы выяснили, что стандартная реализация методов equals() и hashCode() предоставляет из себя весьма скудный и абстрактный инструментарий. Hashcode() выдает нам не всегда уникальный int, а equals по умолчанию, не сравнивает значения полей объекта, а реализован в виде простого ссылочного сравнения.

Читайте также:  Как поменять размер шрифта в ворде

В чем проблема equals() и hashСode() и зачем их нужно переопределять?

Джошуа Блох в книге “Эффективное программирование” писал так, –
“Переопределяя метод equals() всегда переопределяйте hashCode()”

эээ.. так а в чем проблема? В чем проблема equals() и hashcode() и зачем их нужно переопределять? по умолчанию. инструментарий java не обеспечивает алгоритма сравнения объектов на предмет равенства за исключением сравнения ссылок на адреса разделов памяти, зарезервированных под объекты. Так же, по умолчанию java не гарантирует безупречную(полностью удовлетвлряющую требованиям) работу по идентификации объектов с помощью хешкодов. Иными словами, по умолчанию сравнение объектов в java осуществляется сравнением ссылок на адреса памяти. Но проблема в том, что многие важные инструменты java, такие, как инфраструктура коллекций, переопределяют метод для сравнения equals, используя при этом метод hashcode*. Таким образом, если мы не уделяем внимания объектам(не переопределяем вышеназванные методы), которые могут содержаться в этих типах коллекций, мы подспудно, сами того не осознавая, закладываем в код потенциальную возможность ошибок, связанных с несовершенством этих методов.
Например, мы создаем большую HashMap. Так как хешкод по умолчанию может выдавать одинаковые int-ы, то каждый раз когда мы захотим положить объект в hashmap() ( с помощью put())а его хеш вдруг случайно совпадет с хешем уже существующего в мэпе объекта**(так как ключами в HashMap е являются хеши), то мы не добавим новый объект а просто перетрем значение старого новым значением! Вот так, незатейливо произойдет ошибка.

Переопределяем equals()

Итак, по умолчанию, “равенства” объектов как таковых в java нет. Равными могут быть ссылки. Равенство ссылок означает, что они ссылаются на один и тот же объект.
Однако, правило равенства между объектами одного типа, для того, чтобы их можно было сравнивать, можно написать самому. Что и настоятельно следует делать, переопределяя метод equals()! Вспомним сейчас, что если мы переопределяем equals() то нам также следует переопределить и hashCode, ибо как и было рассказано в предыдущем абзаце, многие важные классы пользуются equals() и hashCode().

I. Самый простой способ реализовать equals() и hashCode() в своем классе, воспользоваться авто-генерацией через IDE. Например, в JIdea это можно сделать с помощью alt + insert.

Также есть ряд требований и рекомендаций к реализации equals() и hashCode()

Реализация equals() должна обладать тремя важными свойствами бинарных отношений
1) рефлексивность: a.equals(a)=true;
2) симметричность: if((a.equals(b)))
3) транзитивность: if((a.equals(b))&&(b.equals(c)))
4) equals() должна возвращать неизменное значение, пока какое-либо из свойств объектов не изменяется.
Пока свойства объектов остаются неизменными, они должны оставаться равны.
4) Повторные вызовы метода equals() должны возвращать одно и тоже значение до тех пор, пока какие-либо значения свойств объекта не будут изменены. То есть, если два объекта равны в Java, то они будут равны пока их свойства остаются неизменными.
5) Необходимо проверять объект на null. В случае, если объект = null, equals() должен возвраoать false, а не выкидывать исключение.
6) не помешает проверка соответствия типа(для этого следует использовать getClass())
7) необходимо привести в соответствие equals() и compareTo() для корректной работы hash-коллекций.***

Резюме

a.hashCode() != b.hashCode() -> !(a.equals(b))
a.hashCode() = b.hashCode() -> ((a.equals(b)) / !(a.equals(b)))
Что вносит ущербность в реализацию некоторых коллекций****, основные алгоритмы которых(алгоритмы проверки объектов на предмет равенства) основаны на функциях equals() и hashCode().
Посему в случае, если объекты, планируется содержать в коллекциях, методы которых основаны на данных алгоритмах, рекомендуется переопределять эти методы и приводить их в соответствие бизнес-логике программы

Примечания

**механизм put() класса HashMap несколько сложнее, HashMap содержит массив объектов Enrty, связанный список
***Распространенные ошибки при переопределении equals в Java
****В первую очередь, это коллекции HashSet и HashMap

Adblock
detector