Что такое equals и hashcode java

Java Challengers #4: Сравнение объектов с equals() и hashCode()

В преддверии запуска нового потока по курсу «Разработчик Java» мы продолжаем перевод серии статей Java Challengers, предыдущие части которых можно прочитать по ссылкам ниже:

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

Что такое equals и hashcode java. Смотреть фото Что такое equals и hashcode java. Смотреть картинку Что такое equals и hashcode java. Картинка про Что такое equals и hashcode java. Фото Что такое equals и hashcode java

Без использования equals() и hashCode() для сравнения состояния двух объектов нам нужно писать много сравнений » if «, сравнивая каждое поле объекта. Такой подход делает код запутанным и трудным для чтения. Работая вместе, эти два метода помогают создавать более гибкий и согласованный код.

Исходный код для статьи находится здесь.

Переопределение equals() и hashCode()

Переопределение метода (method overriding) — это приём при котором поведение родительского класса или интерфейса переписывается (переопределяется) в подклассе (см. Java Challengers #3: Полиморфизм и наследование, анг.). В Java у каждого объекта есть методы equals() и hashCode() и для правильной работы они должны быть переопределены.

Это native — метод, который написан на другом языке, таком как Си, и он возвращает некоторый числовой код, связанный с адресом памяти объекта. (Если вы не пишете код JDK, то не важно точно знать, как работает этот метод.)
Примечание переводчика: про значение, связанное с адресом сказано не совсем корректно (спасибо vladimir_dolzhenko). В HotSpot JVM по умолчанию используются псевдослучайные числа. Описание реализации hashCode() для HotSpot, есть здесь и здесь.

Сравнение объектов с equals()

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

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

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

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

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

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

equals() в сравнении с ==

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

Во следующем примере используем переопределенный метод equals() :

Идентификация объектов с hashCode()

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

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

Посмотрим на часть реализации метода add() в HashSet :

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

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

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

Этот принцип в основном используется в коллекциях Set или Hash по соображениям производительности.

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

Таблица 2.Сравнение объектов с hashCode()

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

Решите задачку на equals() и hashCode()

Для начала, внимательно изучите следующий код :

Сначала проанализируйте код, подумайте, какой будет результат. И только потом запустите код. Цель в том, чтобы улучшить ваши навыки анализа кода и усвоить основные концепции Java, чтобы вы могли сделать свой код лучше.

Какой будет результат?.

Что произошло? Понимание equals() и hashCode()

Первый объект в наборе будет вставлен как обычно:

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

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

Ответ

Правильный ответ — B. Вывод будет:

Частые ошибки с equals() и hashCode()

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

Изучите больше о Java

Традиционно жду ваши комментарии и приглашаю на открытый урок, который уже 18 марта проведет наш преподаватель Сергей Петрелевич

Источник

Учимся определять equals() и hashCode();

Что такое equals и hashcode java. Смотреть фото Что такое equals и hashcode java. Смотреть картинку Что такое equals и hashcode java. Картинка про Что такое equals и hashcode java. Фото Что такое equals и hashcode java

Привет. Не для кого наверное уже не секрет, что в Java есть два взаимосвязанных метода это equals() и hashCode() которые надо переопределять, либо не переопределять, если переопределять, то оба сразу.

Поясню для новичков и тех кто может быть забыл.

Метод equals()

Метод equals() используется для сравнения двух объектов, самый часто встречаемый вариант это сравнение строк, что-то типа этого

Метод equals() определен в классе Object (базовом классе для всех классов Java). Его реализация по-умолчанию сравнивает ссылки на объекты в памяти.

Этот метод советуется переопределять в своих классах если требуется сравнение объектов вашего класса. Более того, почти во всех классах Java Core этот метод переопределен.

Этот метод возвращает hash code объекта, нужно это для использования внутри таких структур как Hashtable и HashMap ну и так далее. Если вы переопределяете equals() то вы и обязаны переопределить hashCode() потому что эти два метода взаимосвязаны. Одно из правил определения этих метедов гласит

Если объекты равны (тоесть equals(Object o) == true) то и хэш-коды этих объектов должны быть равны. То есть obj1.equals(obj2) == true? то и obj1.hashCode() == obj2.hashCode()

Правило выше не в коем случае не говорит о том, что теперь объекты можно сравнивать по хэш коду, лучше так же использовать метод equals(). Реализация по-умолчанию метода hashCode() написана на C, если вы посмотрите на этот метод в классе Object, то увидите следующее

native означает что метод этот реализован не на языке Java и проброшен в класс Object через JNI. Что возвращает этот метод по-умолчанию сказать сложно, потому что это зависит от виртуальной машины.

Давайте определимся с правилами написания методов equals() и hashCode().

Легче всего писать метод hashCode(), потому что начиная с JDK 7 появился объект Objects в котором есть метод для создания hash кода на основе полей класса. Выглядит это примерно так

На основе полей класса будет сгенерирован уникальный хэш код. Как я и говорил, с hashCode() все просто, но есть еще и equals(). Давайте определим его.

Давайте теперь рассмотрим метод equals(). Первым делом мы сраниваем объект o с текущим объектом (ссылки)

Это типа некоторая оптимизация, чтобы не производить сравнение значений и так далее если объект сравнивается сам с собой.

Есть соглашение о том, что equals() не должен валиться с NPE если объект o является null’ом. По-этому проверка на null должна быть обязательной. Далее идет сравнение классов, тут неважно какой инстанс объекта у вас создан, getClass() будет одинаковым, вы можете еще увидеть вместо сравнения классов следующую запись

Это в принципе одно и тоже, мне нравится вариант больше со сравнением классов. А дальше мы сравниваем значения полей классов

Эта секция тоже обязательна. В принципе эти методы за вас может генерировать IDE, в Intellij просто жмете сочетание клавишь alt + insert на windows и linux и cmd + n на MacOS. У вас выпаден такая менюшка

Что такое equals и hashcode java. Смотреть фото Что такое equals и hashcode java. Смотреть картинку Что такое equals и hashcode java. Картинка про Что такое equals и hashcode java. Фото Что такое equals и hashcode java

И в этом списке выбирайте просто equals() and hashCode(), после чего вам сгенерируется оба эти метода точно так как я показал выше.

Но знать как это работает обязательно. Так как на собеседовании с листком и ручкой вам IDE не поможет :(.

Ну а если интересно, если вы за чистоту в коде и вам не хочется чтобы в ваших классах явно были определены методы equals и hashCode то можете воспользоваться либой lombok. Она на основе аннотаций генерирует методы equals и hashCode, а так же toString и геттеры с сеттерами. Прошлый пример выглядел бы так.

На этом все, задавайте вопросы в комментариях.

Источник

Что такое equals и hashcode java

Что такое equals и hashcode java. Смотреть фото Что такое equals и hashcode java. Смотреть картинку Что такое equals и hashcode java. Картинка про Что такое equals и hashcode java. Фото Что такое equals и hashcode java

Прежде чем пытаться понять методы equals() и hashCode(), необходимо вспомнить несколько фактов: в Java при сравнении ссылочных переменных сравниваются не сами объекты, а ссылки на объекты, и что все объекты унаследованы от класса Object, который содержит реализацию методов equals() и hashCode() по умолчанию.

Для решения задачи сравнения ссылочных переменных существует стандартное решение – метод equals(). Цель данного метода – определить идентичны ли объекты внутри, сравнив их внутреннее содержание. У класса Object есть своя реализация метода equals, которая просто сравнивает ссылки:

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

У метода equals() есть большой минус – он слишком медленно работает. Для этого был придуман метод hashCode(). Для каждого объекта данный метод возвращает определенное число. Какое именно – это тоже решает разработчик класса, как и в случае с методом equals().

Стандартная реализация метода hashCode() в классе Object:

Вместо того чтобы сравнивать объекты, будем сравнивать их hashCode, и только если hashCode-ы равны, сравнивать объекты посредством equals().

Разработчик, который реализует функцию hashCode(), должен помнить следующее:

1) у двух разных объектов может быть одинаковый hashCode ;

2) у одинаковых объектов (с точки зрения equals()) должен быть одинаковый hashCode ;

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

Важное замечание: при переопределении метода equals(), обязательно нужно переопределить метод hashCode(), с учетом трех вышеописанных правил (Переопределил equals — переопредели и hashCode).

Дело в том, что коллекции в Java перед тем как сравнить объекты с помощью equals всегда ищут/сравнивают их с помощью метода hashCode(). И если у одинаковых объектов будут разные hashCode, то объекты будут считаться разными — до сравнения с помощью equals() просто не дойдет.

Большинство современных IDE помогают переопределять методы equals() и hashCode(). К примеру, в Intellij Idea можно набрать комбинацию клавиш Alt+Ins, и в выпадающем меню Generate выбрать пункт equals and hashCode.

Источник

Javanese Online

Для сравнения объектов HashSet использует два приёма — вычисление хэш-кода (hash code) объекта и точное сравнение объектов.

Есть мнение, что Object#hashCode() возвращает адрес объекта в памяти. Это неверно: в процессе сборки мусора объекты перемещаются, а хэш-код остаётся неизменным. Есть способ заставить JVM считать хэш-код по адресу рождения объекта, но он имеет смысл только для исследовательских целей.

Соответственно, добавление этих объектов будет происходить таким образом:

А вот у класса String эти методы переопределены.

Следовательно, одна строка (или две одинаковых строки) два раза в один Set не добавится:

Переопределение hashCode()

Раз наш класс Something не содержит данных, то все экземпляры равны между собой и имеют одинаковый хэш-код.

Теперь поведение HashSet изменится:

Если объект таки хранит данные (а именно так и бывает), над hashCode и equals придётся поработать.

Для примера возьмём класс Point :

Метод equals

Разберём сгенерированный метод equals :

Методы Float.compare и Double.compare используют для сравнения дробных чисел, т. к. сравнение «в лоб» ( == ) не всегда работает с ними правильно.

Метод hashCode

Хэш-код — это четырёхбайтное целое ( int ). Чтобы посчитать хэш-код объекта, нужно сначала посчитать хэш-коды всех его полей, а затем совместить их.

Для максимальной энтропии (разброса значений) хэш-кодов значения умножаются на 31 перед сложением. Простой множитель 31 найден опытным путём. Подробное разъяснение (en).

В итоге IntelliJ IDEA сгенерирует такой метод hashCode() для класса Point :

Источник

Java equals() and hashCode() Contracts

Last modified: May 3, 2020

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

1. Overview

In this tutorial, we’ll introduce two methods that closely belong together: equals() and hashCode(). We’ll focus on their relationship with each other, how to correctly override them, and why we should override both or neither.

2. equals()

The Object class defines both the equals() and hashCode() methods – which means that these two methods are implicitly defined in every Java class, including the ones we create:

We would expect income.equals(expenses) to return true. But with the Money class in its current form, it won’t.

The default implementation of equals() in the class Object says that equality is the same as object identity. And income and expenses are two distinct instances.

2.1. Overriding equals()

Let’s override the equals() method so that it doesn’t consider only object identity, but rather also the value of the two relevant properties:

2.2. equals() Contract

Java SE defines a contract that our implementation of the equals() method must fulfill. Most of the criteria are common sense. The equals() method must be:

We can look up the exact criteria in the Java SE Docs for the Object class.

2.3. Violating equals() Symmetry With Inheritance

If the criteria for equals() is such common sense, how can we violate it at all? Well, violations happen most often, if we extend a class that has overridden equals(). Let’s consider a Voucher class that extends our Money class:

At first glance, the Voucher class and its override for equals() seem to be correct. And both equals() methods behave correctly as long as we compare Money to Money or Voucher to Voucher. But what happens, if we compare these two objects?

That violates the symmetry criteria of the equals() contract.

2.4. Fixing equals() Symmetry With Composition

To avoid this pitfall, we should favor composition over inheritance.

Instead of subclassing Money, let’s create a Voucher class with a Money property:

And now, equals will work symmetrically as the contract requires.

3. hashCode()

hashCode() returns an integer representing the current instance of the class. We should calculate this value consistent with the definition of equality for the class. Thus if we override the equals() method, we also have to override hashCode().

For some more details, check out our guide to hashCode().

3.1. hashCode() Contract

Java SE also defines a contract for the hashCode() method. A thorough look at it shows how closely related hashCode() and equals() are.

All three criteria in the contract of hashCode() mention in some ways the equals() method:

3.2. Violating the Consistency of hashCode() and equals()

The 2nd criteria of the hashCode methods contract has an important consequence: If we override equals(), we must also override hashCode(). And this is by far the most widespread violation regarding the contracts of the equals() and hashCode() methods.

Let’s see such an example:

The Team class overrides only equals(), but it still implicitly uses the default implementation of hashCode() as defined in the Object class. And this returns a different hashCode() for every instance of the class. This violates the second rule.

Now if we create two Team objects, both with city “New York” and department “marketing”, they will be equal, but they will return different hashCodes.

3.3. HashMap Key With an Inconsistent hashCode()

But why is the contract violation in our Team class a problem? Well, the trouble starts when some hash-based collections are involved. Let’s try to use our Team class as a key of a HashMap:

We would expect myTeamLeader to return “Anne”. But with the current code, it doesn’t.

If we want to use instances of the Team class as HashMap keys, we have to override the hashCode() method so that it adheres to the contract: Equal objects return the same hashCode.

Let’s see an example implementation:

After this change, leaders.get(myTeam) returns “Anne” as expected.

4. When Do We Override equals() and hashCode()?

Generally, we want to override either both of them or neither of them. We’ve just seen in Section 3 the undesired consequences if we ignore this rule.

Domain-Driven Design can help us decide circumstances when we should leave them be. For entity classes – for objects having an intrinsic identity – the default implementation often makes sense.

However, for value objects, we usually prefer equality based on their properties. Thus want to override equals() and hashCode(). Remember our Money class from Section 2: 55 USD equals 55 USD – even if they’re two separate instances.

5. Implementation Helpers

We typically don’t write the implementation of these methods by hand. As can be seen, there are quite a few pitfalls.

One common way is to let our IDE generate the equals() and hashCode() methods.

Apache Commons Lang and Google Guava have helper classes in order to simplify writing both methods.

Project Lombok also provides an @EqualsAndHashCode annotation. Note again how equals() and hashCode() “go together” and even have a common annotation.

6. Verifying the Contracts

If we want to check whether our implementations adhere to the Java SE contracts and also to some best practices, we can use the EqualsVerifier library.

Let’s add the EqualsVerifier Maven test dependency:

Let’s verify that our Team class follows the equals() and hashCode() contracts:

It’s worth noting that EqualsVerifier tests both the equals() and hashCode() methods.

EqualsVerifier is much stricter than the Java SE contract. For example, it makes sure that our methods can’t throw a NullPointerException. Also, it enforces that both methods, or the class itself, is final.

It’s important to realize that the default configuration of EqualsVerifier allows only immutable fields. This is a stricter check than what the Java SE contract allows. This adheres to a recommendation of Domain-Driven Design to make value objects immutable.

If we find some of the built-in constraints unnecessary, we can add a suppress(Warning.SPECIFIC_WARNING) to our EqualsVerifier call.

7. Conclusion

In this article, we’ve discussed the equals() and hashCode() contracts. We should remember to:

Finally, all code examples can be found over on GitHub.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *