Почему мне нужно переопределить методы equals и hashCode в Java?
В Java каждый объект имеет доступ к методу equals(), поскольку он наследуется от класса Object. Если два объекта равны в соответствии с методом equals (Object), то вызов метода hashCode для каждого из двух объектов должен давать одинаковый целочисленный результат. Вы должны переопределить hashCode() в каждом классе, который переопределяет equals(). Несоблюдение этого требования приведет к нарушению общего договора для Object.hashCode(), что предотвратит правильное функционирование вашего класса в сочетании со всеми коллекциями на основе хэшей, включая HashMap, HashSet и Hashtable. Если вы не переопределите hashcode(), то реализация по умолчанию в классе Object будет использоваться коллекциями. Эта реализация дает разные значения для разных объектов, даже если они равны в соответствии с методом equals().
hashCode()()
Метод hashcode() возвращает метод хэш-кода для этой строки. HashCode() используется для bucketing в реализациях Hash, таких как HashMap, HashTable, HashSet и т. Д. Значение, полученное от hashCode(), используется как номер ведра для хранения элементов набора / карты. Этот номер ведра является адресом элемента внутри набора / карты.
equals()
Этот конкретный метод используется для обеспечения равного сравнения между двумя объектами. В Java существует два типа сравнений. Один использует оператор «==», а другой — «equals()».
Как работает hashcode в java
Метод hashCode() в Java используется для получения уникального целочисленного значения, которое представляет объект. Это значение может быть использовано для оптимизации хэш-таблиц и других структур данных.
По умолчанию, метод hashCode() возвращает уникальное значение для каждого объекта, основываясь на его адресе в памяти. Но в большинстве случаев, особенно при работе с коллекциями, требуется иметь более предсказуемый способ вычисления хэш-кода
Для этого, классы должны переопределить метод hashCode() , чтобы он генерировал уникальный код на основе значимых полей объекта. Кроме того, при переопределении метода hashCode() , также необходимо переопределить метод equals() , чтобы он сравнивал значения этих полей.
Например, для класса Person со свойствами name и age , переопределение методов hashCode() и equals() может выглядеть так:
public class Person private String name; private int age; // constructors, getters, setters, etc. @Override public int hashCode() final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; > @Override public boolean equals(Object obj) if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) if (other.name != null) return false; > else if (!name.equals(other.name)) return false; return true; > >
В этом примере метод hashCode() использует константу 31 и вычисляет хэш-код объекта на основе его свойств name и age . Метод equals() сравнивает значения этих свойств.
Таким образом, переопределение метода hashCode() позволяет использовать объекты в хэш-таблицах , таких как HashMap и HashSet , а переопределение метода equals() — сравнивать объекты на равенство в этих структурах.
Почему в Java нужно переопределять методы equals и hashCode?
В программировании на Java часто возникают ситуации, когда необходимо сравнивать объекты на равенство. Для этого в Java есть два метода: equals() и hashCode() . Они являются методами класса Object , от которого наследуются все классы в Java.
Пример
Возьмем простой пример. У нас есть класс Person с двумя полями: name и age .
public class Person < private String name; private int age; public Person(String name, int age) < this.name = name; this.age = age; >>
Попробуем сравнить два объекта этого класса:
Person person1 = new Person("John", 25); Person person2 = new Person("John", 25); System.out.println(person1.equals(person2)); // false
Здесь ожидается, что два объекта будут равны, так как их имя и возраст совпадают. Однако, метод equals() по умолчанию сравнивает ссылки на объекты, а не их содержимое.
Переопределение метода equals
Чтобы корректно сравнивать объекты по содержимому, необходимо переопределить метод equals() . Это позволяет установить свои правила сравнения объектов.
@Override public boolean equals(Object obj)
Теперь метод equals() сравнивает объекты по содержимому:
Person person1 = new Person("John", 25); Person person2 = new Person("John", 25); System.out.println(person1.equals(person2)); // true
Переопределение метода hashCode
С другой стороны, метод hashCode() важен для корректной работы некоторых структур данных, таких как HashSet , HashMap и т.д. Эти структуры используют хеш-код объекта для быстрого доступа к нему. Если два объекта равны по методу equals() , то их хеш-коды должны быть равны.
@Override public int hashCode()
В итоге, переопределение методов equals() и hashCode() позволяет корректно сравнивать объекты и использовать их в коллекциях. Если эти методы не переопределены, могут возникнуть проблемы сравнения объектов и работой коллекций.
Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java?
Зачем переопределять методы equals и hashcode в Java?

Источник: Medium Содержание этой статьи посвящено двум тесно связанным между собой методам: equals() и hashcode() . Вы узнаете, как они взаимодействуют друг с другом и как их правильно переопределять.
Почему мы переопределяем метод equals()?
В Java мы не можем перегружать поведение таких операторов, как == , += , -+ . Они работают согласно заданному процессу. Для примера рассмотрим работу оператора == .
Как работает оператор ==?
Он проверяет, указывают ли две сравниваемые ссылки на один и тот же экземпляр в памяти. Оператор == будет иметь значение true только в том случае, если эти две ссылки представляют один и тот же экземпляр в памяти. Давайте взглянем на пример кода:
public class Person
Допустим, в вашей программе вы создали два объекта Person в разных местах и хотите их сравнить.
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()?

Некоторые структуры данных в 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");

В классе 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");

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