Java что такое хэш значение

Обновлено: 07.07.2024

Что такое hash ? Это некоторое число, генерируемое на основе объекта и описывающее его состояние. Это число вычисляется на основе hash -функции.
Метод int hashCode() является реализация hash -функции для Java -объекта. Возвращаемое число в Java считается hash -кодом объекта.

Объявление метода выглядит так:

Возможно, вас напугала стрчока J9VMInternals.fastIdentityHashCode(this), но пока для простоты считайте, что эта реализация зависит от JVM .

В hash - таблицах или ассоциативных маассивах (их еще называют словари, мапы) важность hashCode -а нельзя недооценивать.

Представим, что у нас есть массив пар ключ-значение. Для поиска по ключу в таком массиве вам надо обойти весь массив, заглянуть в каждую ячейку и сравнить ключ. Долго? Долго!

А теперь вспомним, что хэш - это по сути метаинформация о состоянии объекта, некоторое число. И мы можем просто взять хэш, по нему понять в какой ячейке хранится искомая пара и просто взять ее оттуда, без перебора всего массива! Быстро? Гораздо быстрее, чем просто итерация по всему массиву!

Да, могут быть коллизии - ситуации, когда разным объектам будет сгенерирован одинаковый хэш, но решить коллизию можно через equals и это все еще быстрее, чем проходить весь массив.

По сути, главное преимущество в быстродействии ассоциативных массивов основано на работе с hashCode . Именно за счет хэша мы можем вставлять и получать данные за O(1) , то есть за время пропорциональное вычислению хэш-функции.

Если планируется использовать объекты в качестве ключа в ассоциативном массиве, то переопределять hashCode надо обязательно.

Еще раз посмотрим на метод:

Контракт hashCode предъявляет следующие требования к реализации:

  • Если вызвать метод hashCode на одном и том же объекте, состояние которого не меняли, то будет возвращено одно и то же значение.
  • Если два объекта равны, то вызов hashCode для каждого обязан давать один и тот же результат.

Равенство объектов проверяется через вызов метода equals.

Проще говоря, разный hash -код у двух объектов - это гарантия того, что объекты не равны, в то время как одинаковый hash -код не гарантирует равенства.

Cитуация, когда разные объекты имеют одинаковые hash -код, называется collision или коллизией.

Логично, что коллизии возможны, так как размер int ограничен, да и реализации хэш-функции могут быть далеко не идеальны.

Реализация метода hashCode по-умолчанию возвращает разные значения hash -кодов для разных объектов:

Что это за значения? По-умолчанию hashCode() возвращает значение, которое называется идентификационный хэш (identity hash code). В документации сказано:

This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the Java™ programming language.

Поэтому реализация зависит от JVM .

Более подробно об этом можно прочесть в разделе полезные ссылки.

Пока можете не акцентировать внимание на том, как реализована hash -функция по-умолчанию и запомнить, что для разных объетов будут всегда возвращены разные значения hash -кодов, не взирая на состояние объекта.

Исходя из описания контракта метода можно понять, что hashCode , наряду с equals , играет важную роль в сравнении объектов. По сути он показывает изменилось ли состояние объекта, которое мы используем в equals для сравнения.

Именно поэтому, если вы переопределяете equals , то вы обязаны переопределить hashCode .

Самый очевидный и самый плохой пример как можно переопределить hashCode - это всегда возвращать константу:

Для равных объектов такой метод вернет одно и то же число, что удовлетворяет спецификации. Но, делать так категорически не рекомендуется.

Во-первых, если состояние объекта с такой реализацией hashCode , будет изменено, то это никак не отразится на hashCode . Что уже наталкивает на мысль, что такая реализация не совсем верна.

Во-вторых, такая реализация гарантирует возникновение коллизий всегда. А значит, использовать такой подход - это лишить себя всех преимуществ, которые дает нам использование ассоциативных массивов.

Теперь, когда мы решили, что возвращать всегда одно и то же значение - это плохое решение, давайте поговорим о том, что нужно включать в рассчет hashCode .

Перво-наперво, необходимо исключить избыточные поля, которые не участвуют в equals .

Далее необходимо выбрать базу: число, которое будет основной вычисления hash -кода.

По историческим причинам обычно за базу берут число 31 . Кто-то говорит, что это взято из-за близости к числу 32 , т.е степени двойки 2^5 - 1 . Кто-то утверждает, что был проведен эксперимент и наиболее лучшей базой получились числа 31 и 33 , а 31 понравилось больше.

В целом, вы можете выбрать за базу что хотите, но обычно выбирают 31 . Многие IDE (например, IDEA ) генерят hashCode именно с такой базой.

Правила вычисления hashCode :

Присваиваем переменной result ненулевое значение - базу.

Далее для каждого значимого поля в объекте вычисляем hashCode , по следующим правилам:

Если поле boolean - (f ? 1 : 0)

Если поле byte , char , short или int - (int) f

Если поле long - (int)(f ^ (f >>> 32))

Если поле float , то Float.floatToIntBits(f);

Если поле double , то Double.doubleToLongBits(f) , а затем как с long .

Если поле это ссылка на другой объект, то рекурсивно вызовите hashCode()

Если поле null , то возвращаем 0 .

Если поле это массив, то обрабатываем так, как будто каждый элемент массива - это поле объекта.

После каждого обработанного поля объединяем его hashCode с текущим значением:

Пример приведем с помощью многострадального класса Person :

Также, с версии Java 8+ в классе java.util.Objects есть вспомогательные методы для генерации hashCode :

Использование hashCode в equals

Так как hashCode допускает возникновение коллизий, то использовать его в equals нет смысла. Методы идут в паре, но использование одного в другом не желательно и бесмыссленно.

hashCode призван облегчить поиск объектов в каких-то структурах, например, ассоциативных массивах. equals - это уже непосредственно про сравнение объектов.

Классы-обертки над примитивами

Помните, что типы-обертки, которые по размеру меньше или равны int , возвращают в качестве hashCode свое значение.

Так как Long превосходит int , то, разумеется, возвращать свое значение в качестве hashCode он не может. Вместо этого используется более сложный алгоритм, который работает с значением long (как в примере Person -а).

Помните, что хэш-код массива не зависит от хранимых в нем элементов, а присваивается при создании массива!

Решением является использовать статический метод для рассчета hashCode у массивов: Arrays.hashCode(. ) :

Хэш - это некоторое число, генерируемое на основе объекта и описывающее его состояние. Метод hashCode , наряду с equals , играет важную роль в сравнении объектов. По сути он показывает изменилось ли состояние объекта, которое мы используем в equals для сравнения.

Если планируется использовать объекты в качестве ключа в ассоциативном маассиве, то переопределять hashCode обязательно. Если в классе переопределяется equals , то обязательно надо переопределять hashCode и наоборот.

Эти методы всегда должны определяться парой!

Плохая реализация hash -функции даст вам большое количество коллизий, что сведет на нет все преимущества использования ассоциативных массивов.

Помните, что большинство IDE сейчас легко сгенерируют вам hashCode , чтобы вы не писали его вручную.

Также, существуют сторонние проекты, которые берут кодогенерацию на себя, например, проект lombok. Существуют и сторонние библиотеки, помогающие в вычислении hashCode , например apache commons.



Я недавно начал заниматься программированием, и в этой области для меня много нового. Данная статья рассчитана на начинающих java-программистов, и, надеюсь, поможет в освоении излюбленной темы для собеседований “ hashCode и equals ”.
Хочу сразу предостеречь, что я не являюсь экспертом в данной теме и могу что-то не так понимать, поэтому, если вы нашли ошибку или неточность — свяжитесь со мной.

Что такое хеш-код?

Если очень просто, то хеш-код — это число. На самом деле просто, не так ли? Если более точно, то это битовая строка фиксированной длины, полученная из массива произвольной длины (википедия).

Пример №1
Выполним следующий код:

В результате выполнения программы в консоль выведется целое 10-ти значное число. Это число и есть наша битовая строка фиксированной длины. В java она представлена в виде числа примитивного типа int , который равен 4-м байтам, и может помещать числа от -2 147 483 648 до 2 147 483 647. На данном этапе важно понимать, что хеш-код это число, у которого есть свой предел, который для java ограничен примитивным целочисленным типом int.

Вторая часть объяснения гласит:

Под массивом произвольной длины мы будем понимать объект. В 1 примере в качестве массива произвольной длины у нас выступает объект типа Object .

В итоге, в терминах Java, хеш-код — это целочисленный результат работы метода, которому в качестве входного параметра передан объект.

Этот метод реализован таким образом, что для одного и того-же входного объекта, хеш-код всегда будет одинаковым. Следует понимать, что множество возможных хеш-кодов ограничено примитивным типом int , а множество объектов ограничено только нашей фантазией. Отсюда следует утверждение: “Множество объектов мощнее множества хеш-кодов”. Из-за этого ограничения, вполне возможна ситуация, что хеш-коды разных объектов могут совпасть.

  • Если хеш-коды разные, то и входные объекты гарантированно разные.
  • Если хеш-коды равны, то входные объекты не всегда равны.
Подведём итог:

Сперва, что-бы избежать путаницы, определимся с терминологией. Одинаковые объекты — это объекты одного класса с одинаковым содержимым полей.

Понятие эквивалентности. Метод equals()

Начнем с того, что в java, каждый вызов оператора new порождает новый объект в памяти. Для иллюстрации создадим какой-нибудь класс, пускай он будет называться “BlackBox”.

Пример №2
Выполним следующий код:

Создадим класс для демонстрации BlackBox .

Во втором примере, в памяти создастся два объекта.


Но, как вы уже обратили внимание, содержимое этих объектов одинаково, то есть эквивалентно. Для проверки эквивалентности в классе Object существует метод equals() , который сравнивает содержимое объектов и выводит значение типа boolean true , если содержимое эквивалентно, и false — если нет.


Эквивалентность и хеш-код тесно связанны между собой, поскольку хеш-код вычисляется на основании содержимого объекта (значения полей) и если у двух объектов одного и того же класса содержимое одинаковое, то и хеш-коды должны быть одинаковые (см. правило 2).

Я написал “должно быть”, потому что если вы выполните предыдущий пример, то на самом деле результатом выполнения всех операций будет false . Для пояснения причин, заглянем в исходные коды класса Object .

Класс Object

Как известно, все java-классы наследуются от класса Object . В этом классе уже определены методы hashCode() и equals() .
Определяя свой класс, вы автоматически наследуете все методы класса Object . И в ситуации, когда в вашем классе не переопределены ( @overriding ) hashCode() и equals() , то используется их реализация из Object .

Рассмотрим исходный код метода equals() в классе Object .

При сравнение объектов, операция “ == ” вернет true лишь в одном случае — когда ссылки указывают на один и тот-же объект. В данном случае не учитывается содержимое полей.

Выполнив приведённый ниже код, equals вернет true .



Теперь понято, почему Object.equals() работает не так как нужно, ведь он сравнивает ссылки, а не содержимое объектов.
Далее на очереди hashCode() , который тоже работает не так как полагается.

Заглянем в исходный код метода hashCode() в классе Object :

При вычислении хэш-кода для объектов класса Object по умолчанию используется Park-Miller RNG алгоритм. В основу работы данного алгоритма положен генератор случайных чисел. Это означает, что при каждом запуске программы у объекта будет разный хэш-код.

Получается, что используя реализацию метода hashCode() от класса Object , мы при каждом создании объекта класса new BlackBox() , будем получать разные хеш-коды. Мало того, перезапуская программу, мы будем получать абсолютно разные значения, поскольку это просто случайное число.

Но, как мы помним, должно выполняться правило: “если у двух объектов одного и того же класса содержимое одинаковое, то и хеш-коды должны быть одинаковые ”. Поэтому, при создании пользовательского класса, принято переопределять методы hashCode() и equals() таким образом, что бы учитывались поля объекта.
Это можно сделать вручную либо воспользовавшись средствами генерации исходного кода в IDE. Например, в Eclipse это SourceGenerate hashCode() and equals().

В итоге, класс BlackBox приобретает вид:


Теперь методы hashCode() и equals() работают корректно и учитывают содержимое полей объекта:

Кому интересно переопределение в ручную, можно почитать Effective Java — Joshua Bloch, chapter 3, item 8,9.


Поскольку мы знаем, что Java является объектно-ориентированным языком, следовательно, должен существовать механизм для описания состояния объекта, независимо от того, насколько большим может быть объект. Хеш-функция в Java появилась для выполнения этого требования.

Что такое функция хеширования?

Хэш-функция может быть определена как функция, которая возвращает целочисленное значение, соответствующее объекту. Хэш-функция всегда возвращает одно и то же целочисленное значение для одного и того же объекта. Целочисленное значение, возвращаемое хэш-функцией, называется значением хеш-функции. Ниже приведены важные моменты, касающиеся функции хеширования:

  • Всегда возвращает целое число (4 байта) для объекта.
  • Мы не можем вычислить состояние объекта из хеш-значения, то есть хеш-функции необратимы по своей природе.
  • Два равных объекта будут иметь одинаковое хеш-значение.
  • Два неравных объекта не всегда имеют разные значения Hash.

Применение хэш-функции

Вот общие применения хеш-функций:

1. Структуры данных

Почти каждый язык программирования содержит структуры данных, основанные на хэше. Например, java содержит хэш-таблицу, хэш-карту, хэш-набор, структуры данных дерева, основанные на хэш-функции. Основой этих структур данных является дизайн Key-Value, в котором каждый ключ уникален, тогда как для нескольких ключей может существовать одно и то же значение.

3. Безопасный алгоритм хеширования

Этот алгоритм используется для защиты данных и используется в приложениях и протоколах, таких как Secure Socket Layer (SSL). SHA-0, SHA-1, SHA-2 и SHA-3 являются общими категориями алгоритма безопасного хеширования.

4. Проверка и хранение пароля

Давайте рассмотрим сценарий входа в систему, в котором при вводе пароля для аутентификации пользователя вычисляется хеш-значение введенного пароля и отправляется по сети на сервер, где хранится хеш-код оригинала. Это сделано для того, чтобы при пересылке пароля с сервера на сервер не было обнаружено никакого сниффинга.

5. Работа компилятора

Поскольку в языке программирования используются разные ключевые слова, чтобы различать эти ключевые слова и идентификаторы, компилятор использует хэш-набор, который реализован с использованием хэш-таблицы для хранения всех этих ключевых слов и идентификаторов.

6. Алгоритм Рабина-Карпа

Это алгоритм поиска, который использует хеширование для поиска одного или нескольких шаблонов в заданной строке. Это один из наиболее часто используемых алгоритмов.

7. Сопоставимые и компараторские интерфейсы

Эти интерфейсы содержат функции, которые используются для сравнения двух объектов одновременно. Возвращаемое значение этих функций может быть отрицательным, нулевым или положительным в зависимости от того, является ли данный объект меньше, равен или больше, чем объект, с которым мы сравниваем. Внутренне компаратор и сопоставимые интерфейсы используют хэш-функцию для сравнения объектов друг с другом.

8. Очередь приоритетов

Очередь с приоритетом отличается от обычной очереди, которая следует порядку FIFO (первый пришел первым вышел). В очереди приоритетных элементов расположены в произвольном порядке на основе их приоритета, который реализован внутри с использованием сопоставимых и компаратор, которые стажеры основаны на хэш-функциях.

Проектирование хеш-функций

Вот некоторые общие принципы проектирования для создания хеш-функций:

  • Хэш-функция должна быть эффективно оценена.
  • Хеш-значения, вычисленные по хеш-функциям, должны быть равномерно распределены, это помогает избежать коллизий.
  • Язык программирования Java предоставляет общую функцию хеширования с помощью метода hashCode () в суперклассе Object.

public int hashCode ()(
//Logic goes here
)

Столкновение хэшей в Java

Столкновение хеш-функции происходит, когда два или более объекта возвращают одно и то же хеш-значение. Давайте возьмем пример карты хэша Java, которая хранит данные в парах ключ-значение. Когда мы помещаем объект в хэш-карту, вычисляется хэш-значение ключа, и на основе этого местоположения хэш-значения определяется место хранения объекта значения. Объекты с разными значениями хеша должны входить в разные сегменты. Когда два или более объекта имеют одинаковое значение хеш-функции, они сохраняются в одном и том же месте группы, используя дополнительную структуру данных, называемую связанным списком. Все объекты, имеющие одно и то же значение хеш-функции, объединяются в один связанный список. Этот механизм называется цепочкой. Ниже приведены способы обработки столкновений с помощью хэш-функции:

  • Цепочка: как уже говорилось, идея цепочки заключается в создании связанного списка объектов, имеющих одинаковое значение хеш-функции. Цепочка является простой техникой, но требует дополнительных затрат памяти.
  • Открытая адресация. В этом методе все элементы хранятся в хеш-таблице, в которой каждая запись содержит запись или значение NULL. Когда выполняется поиск элемента, в каждой записи в хеш-таблице выполняется поиск нужной записи, пока не будет найдена требуемая запись или пока не будет сделан вывод, что запись не существует в таблице.

Преимущества хеширования

Ниже приведены преимущества хеширования:

Недостатки хеширования

Помимо преимуществ, существуют также некоторые ограничения хеширования:

  1. Хеширование не может быть реализовано для сортировки данных.
  2. Столкновения хешей практически невозможно избежать, что, в свою очередь, приводит к неэффективности.

Рекомендуемые статьи

Это руководство по функции хеширования в Java. Здесь мы обсуждаем применение хэш-функции наряду с преимуществами и недостатками. Вы также можете посмотреть следующие статьи, чтобы узнать больше -

Методы equals & hashCode: практика использования - 1

Привет! Сегодня мы поговорим о двух важных методах в Java — equals() и hashCode() . Мы встречаемся с ними не впервые: в начале курса JavaRush была небольшая лекция об equals() — прочитай ее, если подзабыл или не встречал ранее. На сегодняшнем же занятии поговорим об этих понятиях подробно — поверь, поговорить есть о чем! И перед тем, как переходить к новому, давай освежим в памяти то, что уже проходили :) Как ты помнишь, обычное сравнение двух объектов через оператор “ == ” — плохая идея, потому что “ == ” сравнивает ссылки. Вот наш пример с машинами из недавней лекции: Вывод в консоль: Казалось бы, мы создали два идентичных объекта класса Car : все поля у двух машин одинаковые, но результат сравнения все равно false. Причина нам уже известна: ссылки car1 и car2 указывают на разные адреса в памяти, поэтому они не равны. Мы же все-таки хотим сравнить два объекта, а не две ссылки. Лучшее решение для сравнения объектов — метод equals() .

Метод equals()

  • мы сравниваем два объекта одного класса
  • это не один и тот же объект
  • мы сравниваем наш объект не c null

Любой объект должен быть equals() самому себе.
Мы уже учли это требование. В нашем методе указано:

Если a.equals(b) == true , то и b.equals(a) должно возвращать true .
Этому требованию наш метод тоже соответствует.

Если два объекта равны какому-то третьему объекту, значит, они должны быть равны друг и другу.
Если a.equals(b) == true и a.equals(c) == true , значит проверка b.equals(c) тоже должна возвращать true.

Результаты работы equals() должны меняться только при изменении входящих в него полей. Если данные двух объектов не менялись, результаты проверки на equals() должны быть всегда одинаковыми.

Неравенство с null .

Для любого объекта проверка a.equals(null) должна возвращать false
Это не просто набор каких-то «полезных рекомендаций», а именно жесткий контракт методов, прописанный в документации Oracle

Метод hashCode()

Если два объекта равны (т.е. метод equals() возвращает true), у них должен быть одинаковый хэш-код.

Иначе наши методы будут лишены смысла. Проверка по hashCode() , как мы и сказали, должна идти первой для повышения быстродействия. Если хэш-коды будут разными, проверка вернет false, хотя объекты на самом деле равны (согласно нашему определению в методе equals() ).

Если метод hashCode() вызывается несколько раз на одном и том же объекте, каждый раз он должен возвращать одно и то же число.

Правило 1 не работает в обратную сторону. Одинаковый хэш-код может быть у двух разных объектов.

Методы equals & hashCode: практика использования - 4

7.5 миллиарда человек. Иными словами, какой бы хороший алгоритм преобразования объектов Man в число мы ни написали, нам просто не хватит чисел. У нас всего 4,5 миллиарда вариантов, а людей намного больше. Значит, как бы мы ни старались, для каких-то разных людей хэш-коды будут одинаковыми. Такая ситуация (совпадение хэш-кодов у двух разных объектов) называется коллизией. Одна из задач программиста при переопределении метода hashCode() — сократить потенциальное число коллизий насколько это возможно. Как же будет выглядеть наш метод hashCode() для класса Man с учетом всех этих правил? Вот так: Удивлен? :) Неожиданно, но если ты посмотришь на требования, увидишь, что мы соблюдаем все. Объекты, для которых наш equals() возвращает true, будут равны и по hashCode() . Если два наших объекта Man будут равны по equals (то есть у них одинаковый dnaCode ), наш метод вернет одинаковое число. Рассмотрим пример посложнее. Допустим, наша программа должна отбирать элитные автомобили для клиентов-коллекционеров. Коллекционирование — штука сложная, и в ней много особенностей. Автомобиль 1963 года выпуска может стоить в 100 раз дороже, чем такой же автомобиль 1964 года. Красный автомобиль 1970 года может стоить в 100 раз дороже, чем синий автомобиль той же марки того же года. В первом случае, с классом Man , мы отбросили большинство полей (т.е. характеристик человека) как незначительные и для сравнения использовали только поле dnaCode . Здесь же мы работаем с очень своеобразной сферой, и незначительных деталей быть не может! Вот наш класс LuxuryAuto : Здесь при сравнении мы должны учитывать все поля. Любая ошибка может стоить сотен тысяч долларов для клиента, поэтому лучше перестраховаться: В нашем методе equals() мы не забыли о все проверках, о которых говорили ранее. Но теперь мы сравниваем каждое из трех полей наших объектов. В этой программе равенство должно быть абсолютным, по каждому полю. А что же с hashCode ? Поле model в нашем классе — строка. Это удобно: в классе String метод hashCode() уже переопределен. Мы вычисляем хэш-код поля model , а к нему прибавляем сумму двух остальных числовых полей. В Java есть одна небольшая хитрость, которая используется для сокращения числа коллизий: при вычислении хэш-кода умножать промежуточный результат на нечетное простое число. Чаще всего используется число 29 или 31. Мы не будем сейчас углубляться в математические тонкости, но на будущее запомни, что умножение промежуточных результатов на достаточно большое нечетное число помогает «размазать» результаты хэш-функции и получить в итоге меньшее число объектов с одинаковым хэшкодом. Для нашего метода hashCode() в LuxuryAuto это будет выглядеть вот так: Подробнее обо всех тонкостях этого механизма можно прочитать в этом посте на StackOverflow, а также у Джошуа Блоха в книге «Effective Java». Напоследок еще один важный момент, о котором стоит сказать. Каждый раз при переопределении equals() и hashCode() мы выбирали определенные поля объекта, которые в этих методах учитывались. Но можем ли мы учитывать разные поля в equals() и hashCode() ? Технически, можем. Но это плохая идея, и вот почему: Вот наши методы equals() и hashCode() для класса LuxuryAuto. Метод hashCode() остался без изменений, а из метода equals() мы убрали поле model . Теперь модель — не характеристика для сравнения двух объектов по equals() . Но при расчете хэш-кода она по-прежнему учитывается. Что же мы получим в результате? Давай создадим два автомобиля и проверим! Ошибка! Использовав разные поля для equals() и hashCode() мы нарушили установленный для них контракт! У двух равных по equals() объектов должен быть одинаковый хэш-код. Мы же получили для них разные значения. Подобные ошибки могут привести к самым невероятным последствиям, особенно при работе с коллекциями, использующими хэш. Поэтому при переопределении equals() и hashCode() правильно будет использовать одни и те же поля. Лекция получилось немаленькой, но сегодня ты узнал много нового! :) Самое время вернуться к решению задач!

В классе Object, который является родительским классом для объектов java, определен метод hashCode(), позволяющий получить уникальный целый номер для данного объекта. Когда объект сохраняют в коллекции типа HashSet, то данный номер позволяет быстро определить его местонахождение в коллекции и извлечь. Функция hashCode() объекта Object возвращает целое число int, размер которого равен 4-м байтам и значение которого располагается в диапазоне от -2 147 483 648 до 2 147 483 647.

Рассмотрим простой пример HashCodeTest.java, который в консоли будет печатать значение hashCode.

Значение hashCode программы можно увидеть в консоли.

По умолчанию, функция hashCode() для объекта возвращает номер ячейки памяти, где объект сохраняется. Поэтому, если изменение в код приложения не вносятся, то функция должна выдавать одно и то же значение. При незначительном изменении кода значение hashCode также изменится.

Функция сравнения объектов equals()

В родительском классе Object наряду с функцией hashCode() имеется еще и логическая функция equals(Object)/ Функция equals(Object) используется для проверки равенства двух объектов. Реализация данной функции по умолчанию просто проверяет по ссылкам два объекта на предмет их эквивалентности.

Рассмотрим пример сравнения двух однотипных объектов Test следующего вида :

Создадим 2 объекта типа Test с одинаковыми значениями и сравним объекты с использованием функции equals().

Результат выполнения программы будет выведен в консоль :

Не трудно было догадаться, что результат сравнения двух объектов вернет «false». На самом деле, это не совсем верно, поскольку объекты идентичны и в real time application метод должен вернуть true. Чтобы достигнуть этого корректного поведения, необходимо переопределить метод equals() объекта Test.

Вот теперь функция сравнения equals() возвращает значение «true». Достаточно ли этого? Попробуем добавить объекты в коллекцию HashSet и посмотрим, сколько объектов будет в коллекции? Для этого в в метод main примера EqualsExample добавим следующие строки :

Однако в коллекции у нас два объекта.

Поскольку Set содержит только уникальные объекты, то внутри HashSet должен быть только один экземпляр. Чтобы этого достичь, объекты должны возвращать одинаковый hashCode. То есть нам необходимо переопределить также функцию hashCode() вместе с equals().

Вот теперь все будет корректно выполняться - для двух объектов с одинаковыми параметрами функция equals() вернет значение «true», и в коллекцию попадет только один объект. В консоль будет выведен следующий текст работы программы :

Таким образом, переопределяя методы hashCode() и equals() мы можем корректно управлять нашими объектами, не допуская их дублирования.

Использование библиотеки commons-lang.jar для переопределения hashCode() и equals()

Процесс формирования методов hashCode() и equals() в IDE Eclipse автоматизирован. Для создания данных методов необходимо правой клавишей мыши вызвать контекстное меню класса (кликнуть на классе) и выбрать пункт меню Source (Class >> Source), в результате которого будет открыто следующее окно со списком меню для класса.


Выбираем пункт меню «Generate hachCode() and equals()» и в класс будут добавлены соответствующие методы.

Библиотека Apache Commons включает два вспомогательных класса HashCodeBuilder и EqualsBuilder для вызова методов hashCode() и equals(). Чтобы включить их в класс необходимо внести следующие изменения.

Примечание : желательно в методах hashCode() и equals() не использовать ссылки на поля, заменяйте их геттерами. Это связано с тем, что в некоторых технологиях java поля загружаются при помощи отложенной загрузки (lazy load) и не доступны, пока не вызваны их геттеры.

Исходный код рассмотренного примера в виде проекта Eclipse можно скачать здесь (263 Kб).

Читайте также: