Каждый объект в программе можно представить в виде некоторого целого числа. Процесс вычисления такого числа называется хешированием, а его результат — хешем. Метод hashCode() генерирует хеш для объектов, чтобы их легче было сортировать и искать.
В дальнейшем, сгенерированный хэш-код поможет пользователю организовать быстрый поиск объекта и доступ к нему в ассоциативных массивах или hash-таблицах. Рассматриваемый метод можно сравнить с сюръекцией. Этот термин означает, что каждый элемент множества B является образом хотя бы одного элемента множества A. Для наглядности ниже будет приведен рисунок.
Как видно, рисунок в точности описывает определение сюръекции. Для всех элементов из множества B существует хотя бы один сопоставленный элемент из множества A. Единственное отличие метода hashCode()
от сюръекции – любой объект может быть обработан данным методом.
В процессе использования рассматриваемого метода могут возникнуть ситуации, когда у разных объектов будет одинаковый hash. Например, как видно по рисунку выше объекты C и V сопоставлены с одним и тем же элементом. Такие случаи называются коллизиями. Исправить их поможет метод equals()
. Он тесно связан с методом hashCode()
. Более подробно мы писали о нем в отдельной статье.
Если в проекте пользователь планирует использовать ассоциативный массив, а в качестве ключей в нем будут объекты, то метод hashCode()
рекомендуется переопределять — для более быстрой и корректной работы. Также его необходимо переопределять в тех случаях, когда было выполнено переопределение метода equals()
. О том, как правильно выполнять переопределение метода hashСode()
в Java, будет рассказано немного позже.
cloud
Объявление метода hashCode()
выглядит следующим образом:
public int hashCode() {
// ...
}
Для правильной реализации рассматриваемого метода в Java определен список следующих требований:
hashCode()
выполняется неоднократно над одним и тем же объектом, то во всех случаях он должен возвращать одинаковое значение, учитывая, что над полями этого объекта не выполнялись какие-либо изменения.equals()
. Он должен вернуть true
, если сравниваемые объекты равны.Представленные выше требования говорят о важности использования рассматриваемого метода вместе с equals()
для сравнения объектов. Второй выполняет сравнение объектов, а первый в свою очередь показывает, изменилось ли состояние объекта. Это еще раз подтверждает, что при переопределении метода equals()
нужно не забывать про переопределение hashCode()
.
Первое, что придет в голову пользователю, который захочет выполнить переопределение hashCode()
в Java, – это сделать возвращение константы:
@Override
public int hashCode() {
return 7;
}
Реализовывать переопределение подобным образом категорически нельзя, и на это есть свои причины:
Исходя из вышеперечисленных недостатков, от подобного переопределения метода необходимо отказаться.
Существует два варианта правильного переопределения метода hashCode()
в Java:
Первый вариант предполагает выполнение следующих правил для переопределения hashCode()
в Java:
equals()
.total
. Зачастую разработчики берут число 31, но вы можете выбрать иное значение. Многие IDE выполняют генерацию хэш-кода именно с этим числом.
Тип поля |
Правило |
boolean |
(f ? 1 : 0) |
char, short, byte или int |
(int) f |
float |
Float.floatToIntBits(f) |
double |
Double.doubleToLongBits(f), а затем (int)(f ^ (f >>> 32)) |
long |
(int)(f ^ (f >>> 32)) |
Ссылка на другой объект |
Рекурсивный вывод метода hashCode() |
Массив |
Обработать каждый элемент массива, как отдельное поле объекта |
null |
return 0 |
compute
) к переменной total
:total = 31 * total + compute;
total
после выполнения всех расчетов.Приведем пример реализации переопределения метода для класса Staff
, используя перечисленные выше правила:
public class Staff {
private String FCs;
private String city;
private int experience;
private double wage;
private String department;
public Staff(String FCs, String city, int experience, double wage, String department) {
this.FCs = FCs;
this.city = city;
this.experience = experience;
this.wage = wage;
this.department = department;
}
@Override
public int hashCode() {
int total = 31;
total = total * 31 + (FCs == null ? 0 : FCs.hashCode());
total = total * 31 + (city == null ? 0 : city.hashCode());
total = total * 31 + experience;
long lwage = Double.doubleToLongBits(wage);
total = total * 31 + (int)(lwage ^ (lwage >>> 32));
total = total * 31 + (department == null ? 0 : department.hashCode());
return total;
}
// Переопределение equals()
// ...
}
Аннотация @Override
перед объявлением рассматриваемого метода проверяет, что переопределяемый метод есть в родительском классе.
Все строковые поля (FCs, city, и department) проверяются на null
перед вызовом их метода hashCode()
, чтобы обеспечить безопасность от NullPointerException
.
Второй вариант предполагает использование вспомогательных методов для генерации хэш-кода, которые доступны благодаря классу java.util.Objects
, начиная с версии Java 8+. Пример данной реализации приведен ниже:
@Override
public int hashCode() {
return Objects.hash(FCs, city, experience, department);
}
У всех стандартных ссылочных типов данных в Java (String, Integer, Double и т. д.) методы equals()
и hashCode()
уже корректно переопределены. Поэтому их возможно спокойно интегрировать в коллекции HashMap, HashSet и другие.
Подготовили для вас выгодные тарифы на облачные серверы
hashCode()
.hashCode()
возвращает целочисленное значение hash-кода для выбранного объекта.hashCode()
рекомендуется переопределять.hashCode()
ускорит работу ассоциативных массивов.equals()
нужно не забывать про переопределение hashCode()
, и наоборот.
Не совсем понятно почему при переопределении hashCode() для поля department (String) использован тернарный оператор, а для других полей с типом String (FCs, city) нет. Возможно, забыли дописать.
Спасибо, что обратили внимание! 💙 Мы поправили блок кода в инструкции.