Методы hashCode() и equals() — базовые методы языка Java. На их основе работают коллекции. Оба эти метода объявлены в классе java.lang.Object. Дочерние классы могут, а в некоторых случаях даже должны переопределять их. Про эти методы я уже как-то писал, но нужно упомянуть об этом ещё раз.
Метод hashCode()
Объявление в классе Object:
1 |
public int hashCode() |
Метод hashCode() возвращает число, которое является хеш-кодом объекта. Реализация по умолчанию в классе Object обычно возвращает адрес объекта, но это в спецификации Java это не закреплено, так что некоторые реализации Java-машин вполне могут возвращать что-нибудь другое.
Метод equals()
Объявление в классе Object:
1 |
public boolean equals(Object obj) |
Возвращает true, если obj равен этому объекту и false в противном случае. Реализация по умолчанию в классе Object просто сравнивает ссылки на объекты, то есть возвращает true в том случае, если обе ссылки указывают на один и тот же объект.
Соглашение между реализациями hashCode() и equals()
В большинстве случаев реализации hashCode() и equals(), которую ваши классы наследуют от Object вам не подойдёт, так как вам нужно, чтобы при вызове equals() сравнивались не ссылки на объекты, а значения полей объектов. Именно поэтому вам нужно будет переопределять equals() для тех классов, которые будут использоваться в качестве ключей коллекций Map и Set. При этом нужно иметь в виду, что при переопределении equals() нужно всегда переопределять hashCode() так, чтобы сохранялись следующие соглашения:
- Если x.equals(y) возвращает true, то hashCode() у обоих экземпляров объектов должны возвращать одинаковые значения.
- Но если x.hashCode() == y.hashCode(), то вовсе не обязательно, чтобы x.equals(y) возвращало true, оно может возвращать как true, так и false.
Как писать hashCode() и equals()?
В большинстве IDE уже есть готовые генераторы hashCode() и equals(), где вам нужно будет только указать поля, которые необходимо учитывать при генерации кода этих методов. При этом вам зачастую на выбор будет предоставлено несколько вариантов генераций:
- Генерация на чистом Java, как оно было раньше
- Генерация с помощью библиотеки Apache Commons Lang.
- Генерация с помощью класса java.util.Objects, который входит в состав Java 7. В классе java.util.Objects есть специальные методы public static int hash(Object... values), public static boolean deepEquals(Object a, Object b) и public static boolean equals(Object a, Object b). Эти методы пришли в Java из библиотеки Guava. Методы с приставкой deep отличаются от обычных тем, что они заходят внутрь массивов и проходят по их элементам, об этом написано чуть ниже.
- Генерация с помощью Guava, где есть методы, аналогичные методам из java.util.Objects.
Всегда имеет смысл посмотреть на сгенерированный IDE код для общего развития. Здесь прослеживается следующая связь:
- Все реализации коллекций и Map-ов в Java имеют переопределённые методы hashCode() и equals(), которые пробегаются по своим элементам для получения результата.
- Массивы в Java не переопределяют hashCode() и equals(). Они используют реализацию из класса Object, которая сравнивает ссылки. Поэтому при построении hashCode() нужно пользоваться статическими методами hashCode() и deepHashCode() из класса java.util.Arrays. При написании методов equals нужно аналогично использовать методы equals() и deepEquals() из класса java.util.Arrays. Методы с приставкой deep здесь отличаются от обычных тем, что в случае, если массив(ы) содержать в качестве элементов другие массивы, то методы без приставки deep будут возвращать значения, основанные на методе из Object, а с приставкой deep будут заходить внутрь этого вложенного массива и проходиться по его элементам.
P. S.
Пока писал эту статью понял, что в моём учебнике по Java не описаны методы класса Object. Странно. Как же я смог их пропустить? Надо как-то восполнить этот пробел.
equals и hashCode это же так банально…
Банально, но вот мне как совсем зелёному очень даже пригодилось и понравилось изложение.