Внимание! Перед прохождением данного урока сначала необходимо пройти следующий раздел, а потом обязательно возвращайтесь сюда, данный урок довольно важен.
Если у любого объекта вызвать метод hashCode
, то его реализация по умолчанию вернет нам число.
Это случайное уникальное число, которое генерируется этим методом по умолчанию.
Переопределять его нужно если мы собираемся запихивать в HashSet
или HashMap
не простые элементы типа char, int, String и т.д., а объекты.
То есть, например, так – set.add(new MyClass(1, 34));.
Вот например мы только что записали в set объект new MyClass(1, 34)
и у нас в классе MyClass пока не переопределен hashCode и если мы теперь запишем в этот же set такой же объект new MyClass(1, 34)
еще раз вот так – set.add(new MyClass(1, 34));
, то в set уже будет ДВА элемента.
А это не должно быть так! Так как мы помним, что ни в HashSet, ни в HashMap одинаковые ключи храниться не должны.
Почему же если мы записываем в hashset идентичные объекты, как ключи, то hashset рассматривает их как разные ключи?
Реализация метода hashCode по умолчанию генерирует разные ключи всем объектам ДАЖЕ ЕСЛИ ОНИ ИДЕНТИЧНЫ по своему содержанию.
То есть если мы создаем объект new MyClass(1, 34)
в первый раз, то у него будет свой hashCode, когда мы создаем new MyClass(1, 34)
второй раз, у него уже будет другой hashCode.
Каждый объект имеет свой hashCode. И hashset добавляет объекты в себя по этому hashCode.
Если hashCode у добавляемых объектов разный, значит эти объекты с наибольшей вероятностью попадут в разные linkedlist-ы в 16 linkedlist-ах, если же они одинаковые, то объекты будут попадать в один и тот же linkedlist.
Как же нам переопределить hashCode, чтобы идентичные объекты всегда записывались в один и тот же linkedlist?
Нам нужно переопределить hashCode так, чтобы он возвращал данные объекта представленные одним восьмибитным числом.
То есть все значения полей объекта нам нужно каким-то образом скомпановать в одно восьмибитное число, которое будет возвращать hashCode
.
И теперь hashCode всех идентичных объектов, например, new MyClass(1, 34)
всегда будет возвращать один и тот же hashCode, так как он является скомпонованными полями объекта, а поля у идентичных объектов new MyClass(1, 34)
одинаковые – 1 и 34.
Но на этом еще не всё. Если объекты одинаковы по hashCode, это только значит, что они попадут в один и тот же самый linkedlist, это еще не обязательно значит, что они одинаковы полностью.
HashSet еще будет сравнивать добавляемый в него объект со всеми уже присутствующими в hashset элементами методом equals и если он НЕ найдет там методом equals идентичный элемент, но при этом объект с таким hashCode уже там присутствует, то в hashset всё равно добавиться этот добавляемый объект и в итоге в нем будет два элемента с одинаковыми hashCode.
Поэтому, чтобы в HashSet не было идентичных объектов, обязательно вместе с hashCode должен быть переопределен и equals.
Переопределение HashCode
Пример программы:
Вывод:

Последовательность добавления элементов в HashSet
Также стоит упомянуть некоторые детали последовательности добавления элементов в HashSet.
При добавлении ключа в HashSet и расчета его hashCode происходит сравнение этого hashCode с hashCode каждого элемента в HashSet, и если hashCode очередного добавляемого объекта отличается от всех остальных уже присутствующих в коллекции, то ключ добавляется СРАЗУ, без сравнения по equals.
Если же такой же hashCode нашелся, то происходит сравнение по equals, и если этим методом не найдет такого же элемента, то произойдет добавление.
В этом уроке был приведен пример стандартного переопределения hashCode.
Таким вот образом его нужно переопределять почти всегда когда вы работаете с hash коллекциями.