что такое хэш код java

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

В Java так устроено, что любой класс, который вы определяете, наследуется от класса Object. Таким образом класс Object является суперклассом любого класса в любой программе.

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

1). Если x.equals(y) == true, то обязательно hashcode(x) == hashcode(y)

2) Если hashcode(x) == hashcode(y), то не обязательно x.equals(y) == true

Отношение эквивалентности (алгебра)

симметричным (для любых x, y выполняется: если x = y, то y = x)

рефлексивным (для любого x выполняется: x = x)

транзитивным (для любых x, y, z выполняется: если x = y и y = z, то x = z)

Метод .equals() в классе Object реализован примерно следующим образом:

Фактически он делает следующее: Он принимает в качестве аргумента ссылочную переменную и проверяет, ссылается ли они на тот же объект (ту же область памяти, если быть точнее), что и объект, к которому мы применили метод .equals().

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

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

К примеру, если классы двух объектов, на которые указывают ссылки, совпадают и все значения их полей совпадают, то эти два объекта эквивалентны между собой. Легко проследить, что такое определение не противоречит математической идеологии.

Конкретную кодовую реализацию я приводить не буду, потому что она не так важна, как сама идея

Это и другие возможные переопределения метода .equals() мало того, что расширяют круг наших возможностей, так ещё и не лишают старых, ведь мы по прежнему имеем возможность проверять, ссылаются ли две ссылки на одну область памяти, используя операнд ==, вместо прежнего .equals()

Сюръекция (алгебра)

Если немного более подробно разобрать это определение, то мы увидим следующее:

Даже несколько элементов из X могут быть сопоставлены одному и тому же элементу из Y (это называется коллизией).

Возможно есть такое элемент из X, и даже возможно не один, что он не сопоставлен никакому элементу из Y. (см. рисунок, всё интуитивно)

Что происходит в java?

Метод .hashcode() как-раз осуществляет сюръекцию. Множеством X выступает множество всевозможных объектов которые мы можем создать, множеством Y выступает область значений типа данных int. Метод .hashcode() вычисляет каким-то скрытым от нас способом целое число, опираясь на объект, к которому применяется.

Единственное отличие метода .hashcode() от сюръекции в том, что любой объект может быть обработан методом .hashcode()

что такое хэш код java. Смотреть фото что такое хэш код java. Смотреть картинку что такое хэш код java. Картинка про что такое хэш код java. Фото что такое хэш код javaЗдесь нет элементов по типу E из пред. рисунка

Насколько я понял, точно так никто в этом и не разобрался. Есть много версий:

Сама функция написана не на Java а вообще на C.

И многие другие. В общем каким-то образом она всё же устроена, но самое главное в том, что стандартная реализация .hashcode() со стандартной реализацией .equals() подчиняются правилу, приведённому в самом начале статьи

Основной причиной для изменения метода .hashcode() является то, что желают изменить .equals(), однако смена стандартной реализации .equals() приводит к нарушению правила из начала статьи

Источник

В предыдущей части, если не читали вот она, мы подробно рассмотрели работу метода equals(), его контракт, ошибки и их исправления. Теперь настала очередь второго попугая-неразлучника – метода hashCode().

При переопределении метода equals() мы всегда должны переопределять метод hashCode(). Метод hashCode() – вычисляет целочисленное значение для конкретного элемента класса, чтобы использовать его для быстрого поиска и доступа к этому элементу в hash-структурах данных, например, HashMap, HashSet и прочих. Почему важно переопределять hashCode() всегда вместе с методом equals()? Развернуто ответим на этот вопрос. Пожалуй, необходимо и достаточно знать два важных аспекта, чтобы понять, почему необходимо делать переопределение методов вместе:

Что такое хеш-таблицы (Hash Tables)?

Хэш – таблицы – это своего рода ассоциативный массив, хранящий значения в виде “ключ-значение”. Рассмотрим работу вставки элемента в хеш-таблицу:

На деле все просто, но если еще раз перечитать контракт hashCode() и equals(), то все становиться немного труднее: возможны коллизии – два разных объекта имеют одинаковый hash-код. Что делать? Эта проблема и ее решение отражены на рисунке выше. Два объекта John Smith и Sandra Dee имеют один и тот же hash-код. Для разрешения это коллизии мы просто берем за структуру bucket направленный список. И сохраняем два значения по одному hash-коду.

Как сломать хеш – таблицу?

При неверной реализации метода hashCode() мы можем легко сломать hash-таблицу. Вернее даже сказать не сломать, а сделать ее вырожденной. Например, переопределив метод hashCode() следующим образом

Источник

Внутренняя работа HashMap в Java

В этой статье мы увидим, как изнутри работают методы get и put в коллекции HashMap. Какие операции выполняются. Как происходит хеширование. Как значение извлекается по ключу. Как хранятся пары ключ-значение.

Как и в предыдущей статье, HashMap содержит массив Node и Node может представлять класс, содержащий следующие объекты:

Теперь мы увидим, как все это работает. Для начала мы рассмотрим процесс хеширования.

Хэширование

Здесь я использую свой собственный класс Key и таким образом могу переопределить метод hashCode() для демонстрации различных сценариев. Мой класс Key:

Здесь переопределенный метод hashCode() возвращает ASCII код первого символа строки. Таким образом, если первые символы строки одинаковые, то и хэш коды будут одинаковыми. Не стоит использовать подобную логику в своих программах.

Этот код создан исключительно для демонстрации. Поскольку HashCode допускает ключ типа null, хэш код null всегда будет равен 0.

Метод hashCode()

Метод equals()

Метод equals используется для проверки двух объектов на равенство. Метод реализованн в классе Object. Вы можете переопределить его в своем собственном классе. В классе HashMap метод equals() используется для проверки равенства ключей. В случае, если ключи равны, метод equals() возвращает true, иначе false.

Корзины (Buckets)

Вычисление индекса в HashMap

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

где n равна числу bucket или значению длины массива. В нашем примере я рассматриваю n, как значение по умолчанию равное 16.

HashMap:
что такое хэш код java. Смотреть фото что такое хэш код java. Смотреть картинку что такое хэш код java. Картинка про что такое хэш код java. Фото что такое хэш код java

Вычислить значение ключа <"vishal">. Оно будет сгенерированно, как 118.

Создать объект node.

Поместить объект в позицию с индексом 6, если место свободно.

Теперь HashMap выглядит примерно так:

что такое хэш код java. Смотреть фото что такое хэш код java. Смотреть картинку что такое хэш код java. Картинка про что такое хэш код java. Фото что такое хэш код java

Вычислить значение ключа <"sachin">. Оно будет сгенерированно, как 115.

Создать объект node.

Поместить объект в позицию с индексом 3, если место свободно.

Теперь HashMap выглядит примерно так:

что такое хэш код java. Смотреть фото что такое хэш код java. Смотреть картинку что такое хэш код java. Картинка про что такое хэш код java. Фото что такое хэш код java

Вычислить значение ключа <"vaibhav">. Оно будет сгенерированно, как 118.

Создать объект node.

Поместить объект в позицию с индексом 6, если место свободно.

В данном случае в позиции с индексом 6 уже существует другой объект, этот случай называется коллизией.

В таком случае проверям с помощью методов hashCode() и equals(), что оба ключа одинаковы.

Если ключи одинаковы, заменить текущее значение новым.

Иначе связать новый и старый объекты с помощью структуры данных «связанный список», указав ссылку на следующий объект в текущем и сохранить оба под индексом 6.

Теперь HashMap выглядит примерно так:

что такое хэш код java. Смотреть фото что такое хэш код java. Смотреть картинку что такое хэш код java. Картинка про что такое хэш код java. Фото что такое хэш код java

[примечание от автора перевода] Изображение взято из оригинальной статьи и изначально содержит ошибку. Ссылка на следующий объект в объекте vishal с индексом 6 не равна null, в ней содержится указатель на объект vaibhav.

Вычислить хэш код объекта <“sachin”>. Он был сгенерирован, как 115.

В нашем случае элемент найден и возвращаемое значение равно 30.

Вычислить хэш код объекта <"vaibhav">. Он был сгенерирован, как 118.

В данном случае он не найден и следующий объект node не равен null.

Если следующий объект node равен null, возвращаем null.

Если следующий объект node не равен null, переходим к нему и повторяем первые три шага до тех пор, пока элемент не будет найден или следующий объект node не будет равен null.

Изменения в Java 8

Для исправления этой проблемы в Java 8 после достижения определенного порога вместо связанных списков используются сбалансированные деревья. Это означает, что HashMap в начале сохраняет объекты в связанном списке, но после того, как колличество элементов в хэше достигает определенного порога происходит переход к сбалансированным деревьям. Что улучшает производительность в худшем случае с O(n) до O(log n).

Источник

Java Challengers #4: Сравнение объектов с equals() и hashCode()

В преддверии запуска нового потока по курсу «Разработчик Java» мы продолжаем перевод серии статей Java Challengers, предыдущие части которых можно прочитать по ссылкам ниже:

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

что такое хэш код java. Смотреть фото что такое хэш код java. Смотреть картинку что такое хэш код java. Картинка про что такое хэш код java. Фото что такое хэш код java

Без использования equals() и hashCode() для сравнения состояния двух объектов нам нужно писать много сравнений » if «, сравнивая каждое поле объекта. Такой подход делает код запутанным и трудным для чтения. Работая вместе, эти два метода помогают создавать более гибкий и согласованный код.

Исходный код для статьи находится здесь.

Переопределение equals() и hashCode()

Переопределение метода (method overriding) — это приём при котором поведение родительского класса или интерфейса переписывается (переопределяется) в подклассе (см. Java Challengers #3: Полиморфизм и наследование, анг.). В Java у каждого объекта есть методы equals() и hashCode() и для правильной работы они должны быть переопределены.

Это native — метод, который написан на другом языке, таком как Си, и он возвращает некоторый числовой код, связанный с адресом памяти объекта. (Если вы не пишете код JDK, то не важно точно знать, как работает этот метод.)
Примечание переводчика: про значение, связанное с адресом сказано не совсем корректно (спасибо vladimir_dolzhenko). В HotSpot JVM по умолчанию используются псевдослучайные числа. Описание реализации hashCode() для HotSpot, есть здесь и здесь.

Сравнение объектов с equals()

Метод equals() используется для сравнения объектов. Чтобы определить одинаковые объекты или нет, equals() сравнивает значения полей объектов:

Во втором сравнении проверяется, является ли переданный объект null и какой у него тип. Если переданный объект другого типа, то объекты не равны.

Наконец, equals() сравнивает поля объектов. Если два объекта имеют одинаковые значения полей, то объекты совпадают.

Анализ вариантов сравнения объектов

Затем снова сравниваем два объекта Simpson :

Наконец, давайте сравним объект Simpson и экземпляр класса Object :

equals() в сравнении с ==

На первый взгляд кажется, что оператор == и метод equals() делают одно и то же, но, на самом деле, они работают по-разному. Оператор == сравнивает, указывают ли две ссылки на один и тот же объект. Например:

Во следующем примере используем переопределенный метод equals() :

Идентификация объектов с hashCode()

Использование equals() и hashCode() с коллекциями

Классы, реализующие интерфейс Set (множество) должны не допускать добавления повторяющихся элементов. Ниже приведены некоторые классы, реализующие интерфейс Set :

Посмотрим на часть реализации метода add() в HashSet :

Перед добавлением нового элемента HashSet проверяет, существует ли элемент в данной коллекции. Если объект совпадает, то новый элемент вставляться не будет.

Рекомендации по использованию equals() и hashCode()

Таблица 1. Сравнение хэш-кодов

Этот принцип в основном используется в коллекциях Set или Hash по соображениям производительности.

Правила сравнения объектов

Таблица 2.Сравнение объектов с hashCode()

Таблица 3. Сравнение объектов с equals()

Решите задачку на equals() и hashCode()

Для начала, внимательно изучите следующий код :

Сначала проанализируйте код, подумайте, какой будет результат. И только потом запустите код. Цель в том, чтобы улучшить ваши навыки анализа кода и усвоить основные концепции Java, чтобы вы могли сделать свой код лучше.

Какой будет результат?.

Что произошло? Понимание equals() и hashCode()

Первый объект в наборе будет вставлен как обычно:

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

Наконец, следующий объект Simpson имеет то же значение имени, что и первый объект. В этом случае объект вставляться не будет:

Ответ

Правильный ответ — B. Вывод будет:

Частые ошибки с equals() и hashCode()

Что нужно помнить о equals() и hashCode()

Изучите больше о Java

Традиционно жду ваши комментарии и приглашаю на открытый урок, который уже 18 марта проведет наш преподаватель Сергей Петрелевич

Источник

Java равно() и хэш-код()

Java equals(), хэш-код Java(), функции Java equals и хэш-кода, Как реализовать методы java equals() и хэш-кода (), метод equals в java, метод хэш-кода в java, метод переопределения Java равен, метод переопределения хэш-кода Java.

Методы Java equals() и hashCode() присутствуют в классе объектов. Таким образом, каждый класс java получает реализацию по умолчанию equals() и hashCode(). В этом посте мы подробно рассмотрим методы java equals() и hashCode ().

Java равно()

Класс объектов, определенный методом equals (), выглядит следующим образом:

Согласно документации java по методу equals (), любая реализация должна соответствовать следующим принципам.

Хэш-код Java()

Хэш-код объекта Java() является собственным методом и возвращает целочисленное значение хэш-кода объекта. Общий контракт метода hashCode() заключается в:

Важность метода equals() и хэш-кода()

Метод Java hashCode() и equals() используются в реализациях на основе хэш-таблиц в java для хранения и извлечения данных. Я подробно объяснил это в разделе “Как работает HashMap в java”?

Реализация equals() и hashCode() должна соответствовать этим правилам.

Когда переопределять методы equals() и hashCode ()?

Когда мы переопределяем метод equals (), почти необходимо переопределить и метод hashCode (), чтобы наша реализация не нарушила их контракт.

Обратите внимание, что ваша программа не будет выдавать никаких исключений, если контракт equals() и hashCode() нарушен, если вы не планируете использовать класс в качестве ключа хэш-таблицы, то это не создаст никаких проблем.

Если вы планируете использовать класс в качестве ключа хэш-таблицы, то необходимо переопределить методы equals() и hashCode ().

Давайте посмотрим, что происходит, когда мы полагаемся на реализацию методов equals() и hashCode() по умолчанию и используем пользовательский класс в качестве ключа HashMap.

Реализация метода equals() и хэш-кода()

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

Мы можем использовать Eclipse для автоматической генерации методов equals() и hashCode ().

Вот автоматически сгенерированные реализации методов equals() и hashCode ().

Обратите внимание, что оба метода equals() и hashCode() используют одни и те же поля для вычислений, поэтому их контракт остается действительным.

Если вы снова запустите тестовую программу, мы получим объект с карты, и программа напечатает 10.

Мы также можем использовать Project Lombok для автоматической генерации эквивалентов и реализации метода хэш-кода.

Что такое столкновение хэшей

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

На рисунке ниже показаны элементы корзины HashMap и то, как связаны их значения() и хэш-код ().

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

Что делать, если мы не реализуем как hashCode (), так и equals()?

Мы уже видели выше, что если hashCode() не реализован, мы не сможем получить значение, потому что HashMap использует хэш-код для поиска корзины для поиска записи.

Если мы используем только хэш-код() и не реализуем функцию equals (), то также значение не будет получено, потому что метод equals() вернет значение false.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *