Как правильно переопределить hashcode java
Переопределение hashCode в Java является важным шагом при реализации методов equals и hashCode для классов. Это необходимо для того, чтобы правильно хранить объекты в хэш-таблицах , например, в HashSet , HashMap и т.д.
Правильное переопределение hashCode в Java должно учитывать все поля, которые участвуют в методе equals , и должно использовать одинаковый алгоритм вычисления для всех объектов класса.
Пример правильного переопределения hashCode :
public class MyClass private String field1; private int field2; // constructor, getters, setters, etc. @Override public boolean equals(Object o) if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyClass myClass = (MyClass) o; return field2 == myClass.field2 && Objects.equals(field1, myClass.field1); > @Override public int hashCode() return Objects.hash(field1, field2); > >
В этом примере hashCode использует метод Objects.hash() , который генерирует хэш-код на основе переданных ему аргументов.
В аргументах перечислены все поля класса, которые участвуют в методе equals . Если вы добавите новое поле в класс, которое также должно участвовать в методе equals , не забудьте добавить его и в hashCode
Кроме того, при переопределении hashCode важно учитывать, что вычисленное значение хэш-кода не должно изменяться во время жизненного цикла объекта, то есть поля, участвующие в методе hashCode, должны быть неизменяемыми. В противном случае, если изменится значение поля объекта, хэш-код также изменится, что может привести к непредсказуемому поведению в хэш-таблицах.
Зачем нам нужно переопределять методы equals и hashcode в Java?
В этом посте мы обсудим, почему важно переопределить equals() а также hashCode() метод в Java.
Пункт 9 в книге Джоша Блоха «Эффективная Java» предлагает всегда переопределять hashCode() метод, если класс переопределяет equals() . В этом посте мы обсудим, почему это необходимо и рекомендуемая практика.
Мы знаем, что два объекта считаются равными, только если их ссылки указывают на один и тот же объект, и если мы не переопределить методы equals и hashCode, объект класса не будет вести себя должным образом в коллекциях на основе хэшей, таких как HashMap , HashSet , а также Hashtable . Это связано с тем, что коллекции на основе хэшей организованы как последовательность сегментов, и значение хеш-кода объекта используется для определения сегмента, в котором будет храниться объект, и тот же хэш-код снова используется для нахождения позиции объекта в ведро. Поиск ключа представляет собой в основном двухэтапный процесс:
- Поиск правильного ведра с помощью hashCode() метод.
- Линейный поиск ключа в ведре с использованием equals() метод.
Теперь давайте возьмем пример, чтобы продемонстрировать необходимость переопределения equals а также hashCode метод в Java:
import java . util . HashSet ;
import java . util . Set ;
class Employee
private String name ;
private int salary ;
public Employee ( String name , int salary )
this . name = name ;
this . salary = salary ;
public String toString ( ) <
public boolean equals ( Object o )
if ( this == o ) <
return true ;
if ( o == null || getClass ( ) != o . getClass ( ) ) <
return false ;
Employee employee = ( Employee ) o ;
if ( salary != employee . salary ) <
return false ;
if ( name != null ? ! name . equals ( employee . name ) : employee . name != null ) <
return false ;
return true ;
public int hashCode ( )
int result = name != null ? name . hashCode ( ) : 0 ;
result = 31 * result + salary ;
return result ;
class Main
// Программа для демонстрации необходимости переопределения `equals` и `hashCode`
// метод в Java
public static void main ( String [ ] args )
Employee e1 = new Employee ( "John" , 80000 ) ;
Employee e2 = new Employee ( "John" , 80000 ) ;
Set
employees . add ( e1 ) ;
employees . add ( e2 ) ;
System . out . println ( employees ) ;
Как видно из сгенерированного вывода, набор содержит только один Employee объект, даже если это два разных Employee добавляются объекты. Это потому, что мы переопределили оба equals() а также hashCode() метод в Employee class, и оба объекта теперь указывают на одно и то же ведро и занимают одно и то же место в ведре.
Теперь давайте обсудим поведение вышеуказанной программы, если equals() метод переопределяется без переопределения hashCode() , или наоборот.
1. Только переопределить equals() без переопределения hashCode() метод
Только переопределение equals() метод без переопределения hashCode() приводит к тому, что два одинаковых экземпляра имеют неравные хэш-коды, что нарушает контракт hashCode (упомянутый в Javadoc), который ясно говорит: если два объекта равны по equals(Object) метод, то вызов метода hashCode для каждого из двух объектов должен давать один и тот же целочисленный результат..
Поскольку реализация hashCode по умолчанию в Object класс возвращает разные целые числа для разных объектов, если только equals() метод переопределен, e1 будет помещен в некоторое ведро и e2 будет помещен в какое-то другое ведро как e1.hashCode() != e1.hashCode() . Хотя оба e1 а также e2 равны, они не относятся к одному и тому же сегменту, и оба находятся в коллекции как отдельные ключи.
Стоит отметить, что если экземпляр класса никогда не используется ни в каких коллекциях на основе хэшей, то не имеет большого значения, если hashCode() переопределяется или нет.
2. Только переопределение hashCode() без переопределения equals() метод
Если мы только переопределим hashCode() метод, оба e1 а также e2 будут хэшировать в одно и то же ведро, поскольку они производят один и тот же хеш-код. Но так как equals() метод не переопределяется, когда заданы хэши e2 и перебирает ведро, ища, есть ли Сотрудник e такой, что e2.equals(e) это правда, он не найдет ни одного e2.equals(e1) будет ложным.
Обратите внимание, что хотя одинаковые объекты должны иметь одинаковые хэш-коды, обратное неверно. Вполне допустимо переопределить hashCode() без переопределения equals() поскольку объекты с одинаковыми хеш-кодами не обязательно должны быть равными.
Вот и все, почему нам нужно переопределить методы equals и hashcode в Java.
Оценить этот пост
Средний рейтинг 4.57 /5. Подсчет голосов: 44
Голосов пока нет! Будьте первым, кто оценит этот пост.
Сожалеем, что этот пост не оказался для вас полезным!
Расскажите, как мы можем улучшить этот пост?
Как работает 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() — сравнивать объекты на равенство в этих структурах.
Методы .equals и .hashcode в Java. Отличия реализации по умолчанию от реализации на практике
Сразу же скажу, что статья во многом опирается базовые понятия алгебры, которые к великому счастью легко и быстро осознаются при помощи всего-лишь метода внимательного разглядывания. Поехали.
В Java так устроено, что любой класс, который вы определяете, наследуется от класса Object. Таким образом класс Object является суперклассом любого класса в любой программе.
Это означает, что абсолютно любой класс содержит методы, которые определены в классе Object. Методы .equals() и .hashcode() — одни из них.
Прежде всего я должен описать главные правила для любых реализаций этих двух методов, которые нужно обязательно соблюдать, запомнить как аксиому:
1). Если x.equals(y) == true, то обязательно hashcode(x) == hashcode(y)
2) Если hashcode(x) == hashcode(y), то не обязательно x.equals(y) == true
Метод .equals()
Отношение эквивалентности (алгебра)
Прежде чем поговорить о методе .equals, я бы хотел рассказать, что такое отношение эквивалентности с точки зрения алгебры (пока-что забудьте про программирование).
Отношение эквивалентности — это бинарное (бинарное — значит между двумя) отношение, которое является:
Таким образом, если на множестве определено отношение эквивалентности, множество можно разделить на подмножества — классы эквивалентности.
Каждый класс эквивалентности содержит внутри себя только те элементы, которые эквиваленты (более формально — находятся в отношении эквивалентности) между собой.
Реализация .equals() по умолчанию
Метод .equals() в классе Object реализован примерно следующим образом:
public boolean equals(Object x)
Фактически он делает следующее: Он принимает в качестве аргумента ссылочную переменную и проверяет, ссылается ли они на тот же объект (ту же область памяти, если быть точнее), что и объект, к которому мы применили метод .equals().
Таким образом, стандартная реализация .equals() выстраивает отношение эквивалентности, которое можно описать так: две ссылки эквивалентны, если они ссылаются на одну и ту же область памяти.
Такая реализация не противоречит математической идеологии, описанной выше. Однако на практике метод .equals() часто переопределяют в подклассах.
Как и зачем переопределяют метод .equals()?
Очевидно, гораздо более применимой будет возможность сравнивать объекты по какому-нибудь другому критерию. Часто метод .equals() переопределяют так, чтобы он сравнивал объекты по значениям их полей.
К примеру, если классы двух объектов, на которые указывают ссылки, совпадают и все значения их полей совпадают, то эти два объекта эквивалентны между собой. Легко проследить, что такое определение не противоречит математической идеологии.
Конкретную кодовую реализацию я приводить не буду, потому что она не так важна, как сама идея
Это и другие возможные переопределения метода .equals() мало того, что расширяют круг наших возможностей, так ещё и не лишают старых, ведь мы по прежнему имеем возможность проверять, ссылаются ли две ссылки на одну область памяти, используя операнд ==, вместо прежнего .equals()
return(ob1 == ob2);
Метод .hashcode()
Сюръекция (алгебра)
Сюръекция — сопоставление элементам множества X элементов второго множества Y, при котором для любого элемента из Y есть хотя-бы один сопоставленный элемент из X.
Если немного более подробно разобрать это определение, то мы увидим следующее:

- Даже несколько элементов из X могут быть сопоставлены одному и тому же элементу из Y (это называется коллизией).
- Возможно есть такое элемент из X, и даже возможно не один, что он не сопоставлен никакому элементу из Y. (см. рисунок, всё интуитивно)
Что происходит в java?
Метод .hashcode() как-раз осуществляет сюръекцию. Множеством X выступает множество всевозможных объектов которые мы можем создать, множеством Y выступает область значений типа данных int. Метод .hashcode() вычисляет каким-то скрытым от нас способом целое число, опираясь на объект, к которому применяется.
Единственное отличие метода .hashcode() от сюръекции в том, что любой объект может быть обработан методом .hashcode()

Реализация .hashcode() по умолчанию?
Насколько я понял, точно так никто в этом и не разобрался. Есть много версий:
- Значение .hashcode() — это область памяти, где лежит объект
- Значение .hashcode() — это число, создаваемое генератором случайных чисел в какой-то момент
- Сама функция написана не на Java а вообще на C.
И многие другие. В общем каким-то образом она всё же устроена, но самое главное в том, что стандартная реализация .hashcode() со стандартной реализацией .equals() подчиняются правилу, приведённому в самом начале статьи
Как и зачем переопределяют метод .hashcode()?
Основной причиной для изменения метода .hashcode() является то, что желают изменить .equals(), однако смена стандартной реализации .equals() приводит к нарушению правила из начала статьи
Второстепенной причиной для изменения метода .hashcode() является то, что желают изменить вероятность коллизии (эта причина встречается реже)
Конец 🙂