Перегрузка методов equals() и hashCode() в Java
Переопределение методов equals() и hashCode() в Java
- Рефлексивность: Объект должен равняться себе самому.
- Симметричность: если a.equals(b) возвращает true , то b.equals(a) должен тоже вернуть true .
- Транзитивность: если a.equals(b) возвращает true и b.equals(c) тоже возвращает true , то c.equals(a) тоже должен возвращать true .
- Согласованность: повторный вызов метода equals() должен возвращать одно и тоже значение до тех пор, пока какое-либо значение свойств объекта не будет изменено. То есть, если два объекта равны в Java, то они будут равны пока их свойства остаются неизменными.
- Сравнение null : объект должны быть проверен на null . Если объект равен null , то метод должен вернуть false , а не NullPointerException . Например, a.equals(null) должен вернуть false .
Соглашение между equals и hashCode в Java
- Если объекты равны по результатам выполнения метода equals , тогда их hashcode должны быть одинаковыми.
- Если объекты не равны по результатам выполнения метода equals , тогда их hashcode могут быть как одинаковыми, так и разными. Однако для повышения производительности, лучше, чтобы разные объекты возвращали разные коды.
Как переопределять метод equals в Java
Проверьте объект на null , а также проверьте, чтобы объекты были одного типа. Не делайте проверку с помощью instanceof так как такая проверка будет возвращать true для подклассов и будет работать правильно только в случае если ваш класс объявлен как immutable . Вместо этого можно использовать getClass() ;
Объявите переменную типа, который вы сравниваете, и приведите obj к этому типу. Потом сравнивайте каждый атрибут типа начиная с численных атрибутов (если имеются) потому что численные атрибуты проверяются быстрей. Сравнивайте атрибуты с помощью операторов И и ИЛИ (так называемые short-circuit logical operators ) для объединения проверок с другими атрибутами.
Распространенные ошибки при переопределении equals в Java
Вместо того, чтобы переопределять метод equals (Override) программист перегружает его (Overload) Синтаксис метода equals() в классе Object определен как public boolean equals(Object obj) , но многие программисты ненароком перегружают метод: public boolean equals(Person obj) — вместо Object в качестве аргумента используют имя своего класса (напр. Person). Эту ошибку сложно обнаружить из-за static binding . Таким образом, если вы вызовете этот метод для объекта своего класса, то метод не просто скомпилируется, а даже сделает это корректно. Однако, если вы положите ваш объект в коллекцию, например ArrayList и вызовете метод contains() , работа которого основана на методе equals() , то метод contains не сможет обнаружить ваш объект.
При переопределении метода equals() не проверять на null переменные, что в конечном итоге заканчивается NullPointerException при вызове equals() . Ниже представлен корректный код.
Третья распространенная ошибка это не переопределять метод hashCode() , а только equals() . Вы обязаны переопределять оба метода equals() и hashCode() в Java. Метод hashCode используется в hash -коллекциях(например HashSet ), и чем меньше будет коллизий (одинаковый код при разных объектах) тем эффективнее эти коллекции будут работать с объектами вашего класса.
Последняя распространенная ошибка программистов в том, что при переопределении метода equals() не сохраняется соответствие между методами equals() и compareTo() , что является неформальным требованием для избежания хранения дубликатов в Set (SortedSet, TreeSet) .
Подсказки как писать в Java метод equals
Большинство IDE такие как NetBeans, Eclipse и IntelliJ IDEA обеспечивают поддержку генерации методов equals() и hashCode() . В Eclipse нажмите правую кнопку -> source -> generate equals() и hashCode() .
Если в классе есть уникальный бизнес-ключ, то будет достаточно сделать проверку только на равенство этих полей. Как в нашем примере “id” — уникальный номер для каждого Person.
При переопределении hashCode() в Java удостоверьтесь в использовании всех полей, что были использованы в методе equals() .
String и классы-оболочки такие как Integer , Float и Double переопределяют метод equals() , но StringBuffer не переопределяет.
При любой возможности делайте поля immutable используя final переменные в Java.
При сравнении String объектов используйте equals() вместо оператора == .
Два объекта которые логически равны, но загружены из разных ClassLoader не могут быть равными. Помните, что проверка с помощью getClass() вернет false если класс-загрузчик разный.
Используйте @Override аннотацию также для метода hashCode , так как это предупреждает неуловимые ошибки, например возвращаемое значение метода int , однако некоторые программисты возвращают long .
О сравнении объектов с помощью методов Java equals() и hashcode()
Какова связь между методами 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() переопределяется для переменной overridenHomer. Поле name в обоих объектах Simpson- “Homer”. Но метод hashcode() возвращает другое значение для overriddenHomer. В этом случае результат работы 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() не переопределяются при использовании хэш-коллекций, коллекция будет иметь повторяющиеся элементы.
Пожалуйста, оставляйте ваши мнения по текущей теме статьи. Мы крайне благодарны вам за ваши комментарии, дизлайки, отклики, подписки, лайки!
Пожалуйста, оставляйте свои отзывы по текущей теме статьи. За комментарии, подписки, дизлайки, отклики, лайки низкий вам поклон!
Метод equals() в Java
![]()
Основные принципы при проектировании метода equals :
- Рефлексивность: для любых не-null объектов x, x.equals(x) должен возвращать true .
- Симметричность: для любых не-null объектов x и y, x.equals(y) должен возвращать true , если и только если y.equals(x) возвращает true .
- Транзитивность: для любых не-null объектов x, y, и z, если x.equals(y) возвращает true и y.equals(z) возвращает true , тогда x.equals(z) должен возвращать true .
- Постоянство: повторный вызов метода equals() должен возвращать одно и тоже значение до тех пор, пока какое-либо значение свойств объекта не будет изменено. То есть, если два объекта равны, то они будут равны пока их свойства остаются неизменными.
- Для любых не-null объектов x, x.equals(null) должно возвращать false .
Также стоит обратить внимание на то, что аргументом метода equals всегда является объект класса Object . Например, переопределяя метод для класса Person , мы должны использовать следующую сигнатуру: public boolean equals(Object person) . Зачастую начинающими допускается ошибка и используется сигнатура public boolean equals(Person person) , что приводит к перегрузке метода equals .
Метод equals взаимосвязан с методом hashCode : для одинаковых объектов значение, возвращаемое методом hashCode , должно быть одинаково. Поэтому зачастую переопределяются оба метода.
![]()
Приведу пример, по которому все будет понятно, также не надо забывать, что при переопределении метода equals() необходимо переопределить метод hashCode(). Если метод equals() возвращает истину для двух объектов, то их хэш-код должен быть одинаковым. Обратное утверждение не верно.
![]()
Прошу пояснить код. В частности, вот эту строку
Основной вопрос состоит в следующем. Как Вы думаете, что делает эта строчка процитированного кода? Да, там вызывается метод equals родительского класса. А кто у нас у класса в данном случае родитель? Если верить спецификации, то это java.lang.Object. Значит сначала мы в первой строке Вашего кода вызываем метод equals класса Object. Как Вы думаете, что он делает? Вроде как это известная информация, но для большей достоверности вот цитата из исходников
То есть он возвращает true только в том случае, когда у нас ссылки на объект совпадают. Если же у нас 2 отдельных объекта, которые хранятся в памяти в разных местах (пусть у них содержимое одинаковое), ссылки на них не совпадают! А что это значит? Это значит, что благодаря строке if(!super.equals(other)) return false; Ваш метод equals будет возвращать true , только если мы будем сравнивать объект сам с собой! Наверное, это не то, чего бы всем хотелось, не так ли? Думаю, эту строку надо убрать и тогда это будет больше похоже на правду. Но вообще каждый раз при написании своего equals надо принимать во внимание тот факт, что когда-то создатели Java и класса java.lang.Object в частности написали условия, которым должен удовлетворять переопределённый метод equals, найти их можно в исходниках, либо здесь.
И ещё заметка относительно hashCode . Вы упомянули только одно из требований котракта по hashCode : если 2 объекта считаются равными (equals возвращает true), то hashCode для них должен возвращать одинаковое значение. Дальше
на самом деле в контракте, найти можно — здесь стоит продолжение: «However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables». То есть надо принимать этот факт во внимание, как и ещё один пункт из этого же контракта.