Методы equals() и hashcode() в языке Java
Разберём equals и hashCode в Java: поговорим о различии методов, контрактов, а также посмотрим на реальные примеры использования.
32К открытий40К показов
Методы equals и hashCode в Java в чём-то очень схожи, и даже вместе генерируются средствами IDE, таких как IntelliJ IDEA. Что в них общего, каковы отличия, и что будет, если использовать только один из методов?
Знаете, чем отличаются ==, equals и hashCode?
Да
Нет
- Метод equals()
- Контракт equals()
- Использование equals
- Метод hashcode()
- Контракт hashCode()
- Использование hashCode
- Почему equals и hashCode в Java переопределяются вместе
Метод equals() в Java
Как вы наверняка знаете, сравнение посредством ==
в Java сравнивает ссылки, но объекты таким образом не сравнить. Следующий пример подобного сравнения двух строк вернёт false
:
Пусть значения и одинаковы, но переменные String
указывают на разные объекты.
Тут-то в игру и вступает метод equals()
, предусмотренный в Java для сравнения именно объектов. Данный метод проверяет два объекта одного происхождения на логическую равность.
То есть, сравнивая два объекта, программисту необходимо понять, эквивалентны ли их поля. При этом необязательно все поля должны быть идентичными, поскольку метод equals()
подразумевает именно логическое равенство.
Контракт equals() в Java
Используя equals
, мы должны придерживаться основных правил, определённых в спецификации Java:
- Рефлексивность —
x.equals(x)
возвращаетtrue
. - Симметричность —
x.equals(y) <=> y.equals(x)
. - Транзитивность —
x.equals(y) <=> y.equals(z) <=> x.equals(z)
. - Согласованность — повторный вызов
x.equals(y)
должен возвращать значение предыдущего вызова, если сравниваемые поля не изменялись. - Сравнение null —
x.equals(null)
возвращаетfalse
.
Использование equals
Предположим, у нас есть класс Programmer
, в котором предусмотрены поля с должностью и зарплатой:
В переопределённом методе equals()
обе переменные участвуют в проверке. Также вы всегда можете убрать ту переменную, которую не хотите проверять на равенство.
Зачастую метод equals в Java определяется вместе с hashCode, но здесь мы рассмотрим первый метод отдельно:
Определим объекты programmer1
и programmer2
типа Programmer
с одинаковыми значениями. При их сравнении с помощью ==
вернётся false
, так как это разные объекты. Если же мы используем для сравнения метод equals()
, вернётся true
:
А вот такой результат мы получим, если хотя бы одна переменная (обозначенное поле объекта) из метода equals()
не совпадёт:
Метод hashcode() в Java
Наконец, мы дошли до сравнения методов equals и hashCode в языке Java.
Фундаментальное отличие в том, что hashCode()
— это метод для получения уникального целочисленного номера объекта, своего рода его идентификатор. Благодаря хешу (номеру) можно, например, быстро определить местонахождение объекта в коллекции.
Это число используется в основном в хеш-таблицах, таких как HashMap
. При этом хеш-функция получения числа на основе объекта должна быть реализована таким образом, чтобы обеспечить равномерное распределение элементов по хэш-таблице. А также минимизировать возможность появления коллизий, когда по разным ключам функция вернёт одинаковое значение.
В случае Java, метод hashCode()
возвращает для любого объекта 32-битное число типа int
. Сравнить два числа между собой гораздо быстрее, чем сравнить два объекта методом equals()
, особенно если в нём используется много полей.
Контракт hashCode() в Java
- Повторный вызов
hashCode
для одного и того же объекта должен возвращать одинаковые хеш-значения, если поля объекта, участвующие в вычислении значения, не менялись. - Если
equals()
для двух объектов возвращаетtrue
,hashCode()
также должен возвращать для них одно и то же число. - При этом неравные между собой объекты могут иметь одинаковый
hashCode
.
Использование hashCode
Вернёмся к нашему классу Programmer
. По-хорошему, вместе с equals()
должен быть использован и метод hashCode():
Почему equals и hashCode в Java переопределяются вместе
Сперва производится сравнение по хешу, чтобы понять, совпадают ли объекты, а только после подключается equals
, чтобы определить, совпадают ли значения полей объекта.
Рассмотрим два сценария.
1. equals есть, hashCode нет
С точки зрения метода equals два объекта будут логически равны, но по hashCode они не будут иметь ничего общего. Таким образом, помещая некий объект в хэш-таблицу, мы рискуем не получить его обратно по ключу:
2. hashCode есть, equals нет
Метод equals
по умолчанию сравнивает указатели на объекты, определяя, ссылаются ли они на один и тот же объект. Следовательно, пример из предыдущего пункта по идее должен выполняться. Но мы по-прежнему не сможем найти наш объект в хэш-таблице.
Для успешного поиска объекта в хэш-таблице помимо сравнения хэш-значений ключа используется также определение логического равенства ключа с искомым объектом.
Читайте также об условных операторах в Java.
32К открытий40К показов