В соответствии с моим пониманием я думаю:
- Вполне допустимо, чтобы два объекта имели одинаковый хэш-код.
- Если два объекта равны (используя метод equals ()), они имеют одинаковый хэш-код.
- Если два объекта не равны, они не могут иметь одинаковый хеш-код
Я прав?
Теперь, если я прав, у меня есть следующий вопрос: HashMap
внутренне использует хеш-код объекта. Итак, если два объекта могут иметь одинаковый хеш-код, то как можно HashMap
отслеживать, какой ключ он использует?
Может кто-нибудь объяснить, как HashMap
внутренне использует хеш-код объекта?
java
hashmap
hashcode
hash-function
Акшай
источник
источник
Ответы:
Хэш-карта работает следующим образом (это немного упрощено, но иллюстрирует основной механизм):
У него есть несколько «блоков», которые он использует для хранения пар ключ-значение. Каждый блок имеет уникальный номер - это то, что идентифицирует блок. Когда вы помещаете пару «ключ-значение» в карту, хэш-карта будет смотреть на хеш-код ключа и сохранять пару в сегменте, идентификатором которого является хеш-код ключа. Например: хэш-код ключа - 235 -> пара сохраняется в номере корзины 235. (Обратите внимание, что в одной корзине может храниться более одной пары ключ-значение).
Когда вы просматриваете значение в hashmap, давая ему ключ, оно сначала смотрит на хеш-код ключа, который вы дали. Затем хэш-карта будет искать в соответствующем сегменте, а затем сравнивать ключ, который вы дали, с ключами всех пар в сегменте, сравнивая их с
equals()
.Теперь вы можете видеть, как это очень эффективно для поиска пар ключ-значение на карте: по хэш-коду ключа хэш-карта сразу знает, в каком сегменте искать, так что ему нужно только проверить, что находится в этом сегменте.
Рассматривая вышеупомянутый механизм, вы также можете увидеть, какие требования необходимы для
hashCode()
иequals()
методам ключей:Если два ключа совпадают (
equals()
возвращаетсяtrue
при сравнении), ихhashCode()
метод должен возвращать одно и то же число. Если ключи нарушают это, то равные ключи могут храниться в разных сегментах, и хэш-карта не сможет найти пары ключ-значение (потому что он будет выглядеть в одном и том же сегменте).Если два ключа различны, то не имеет значения, совпадают ли их хэш-коды или нет. Они будут храниться в одном и том же сегменте, если их хеш-коды одинаковы, и в этом случае хеш-карта будет использовать
equals()
для их разделения .источник
hashCode()
метод возвращает различный хэш - коду, тоequals()
иhashCode()
методы ключевого класса нарушают договор , и вы получите странные результаты при использовании этих ключей вHashMap
.HashMap
найдите исходный код , который вы можете найти в файлеsrc.zip
в вашем каталоге установки JDK.Ваше третье утверждение неверно.
Совершенно законно, что два неравных объекта имеют одинаковый хеш-код. Он используется
HashMap
в качестве «фильтра первого прохода», так что карта может быстро найти возможные записи с указанным ключом. Затем ключи с одинаковым хеш-кодом проверяются на равенство с указанным ключом.Вы не хотели бы требовать, чтобы два неравных объекта не могли иметь одинаковый хеш-код, иначе это ограничило бы вас 32 32 возможными объектами. (Это также означает, что разные типы не могут даже использовать поля объекта для генерации хеш-кодов, так как другие классы могут генерировать тот же хеш.)
источник
HashMap
это массивEntry
объектов.Считайте
HashMap
просто массивом объектов.Посмотрите, что это
Object
такое:Каждый
Entry
объект представляет пару ключ-значение. Полеnext
ссылается на другойEntry
объект, если в корзине более одногоEntry
.Иногда может случиться так, что хеш-коды для двух разных объектов совпадают. В этом случае два объекта будут сохранены в одном сегменте и будут представлены в виде связанного списка. Точкой входа является недавно добавленный объект. Этот объект относится к другому объекту с
next
полем и так далее. Последняя запись относится кnull
.Когда вы создаете конструктор
HashMap
по умолчаниюМассив создается с размером 16 и балансировкой нагрузки по умолчанию 0,75.
Добавление новой пары ключ-значение
hash % (arrayLength-1)
которой должен быть размещен элемент (номер корзины)HashMap
, значение будет перезаписано.Если в корзине уже есть хотя бы один элемент, добавляется новый и помещается в первую позицию корзины. это
next
поле относится к старому элементу.делеция
hash % (arrayLength-1)
Entry
. Если нужный элемент не найден, вернутьnull
источник
hash % (arrayLength-1)
это было быhash % arrayLength
. Но это на самом делеhash & (arrayLength-1)
. То есть потому, что он использует степени two (2^n
) для длины массива, принимаяn
младшие значащие биты.int
разумеется, отрицательное число, выполнение по модулю отрицательного числа даст вам отрицательное числоВы можете найти отличную информацию на http://javarevisited.blogspot.com/2011/02/how-hashmap-works-in-java.html
Подвести итоги:
HashMap работает по принципу хеширования
put (ключ, значение): HashMap сохраняет объект ключа и значения как Map.Entry. Hashmap применяет хеш-код (ключ) для получения корзины. если есть столкновение, HashMap использует LinkedList для хранения объекта.
get (key): HashMap использует хеш-код Key Object для определения местоположения сегмента, а затем вызывает метод keys.equals (), чтобы определить правильный узел в LinkedList и вернуть объект связанного значения для этого ключа в Java HashMap.
источник
Вот примерное описание
HashMap
механизма дляJava 8
версии (он может немного отличаться от Java 6) .Структуры данных
Хеш-значение рассчитывается с помощью
hash()
ключа и определяет, какой сегмент хеш-таблицы использовать для данного ключа.Когда количество элементов в корзине мало, используется однократно связанный список.
Когда количество элементов в ведре велико, используется красно-черное дерево.
Классы (внутренние)
Map.Entry
Представлять один объект на карте, ключ / значение объекта.
HashMap.Node
Версия связанного списка узла.
Это может представлять:
Потому что у него есть свойство хеша.
HashMap.TreeNode
Древовидная версия узла.
Поля (внутренние)
Node[] table
Таблица ведра, (глава связанных списков).
Если корзина не содержит элементов, то она пуста, поэтому занимает только место ссылки.
Set<Map.Entry> entrySet
Набор сущностей.int size
Количество объектов.
float loadFactor
Укажите, насколько полна хеш-таблица, до изменения размера.
int threshold
Следующий размер для изменения размера.
Формула:
threshold = capacity * loadFactor
Методы (внутренние)
int hash(key)
Рассчитать хеш по ключу.
Как сопоставить хэш с ведром?
Используйте следующую логику:
О вместимости
В хеш-таблице, емкость означает количество сегментов, из которого можно получить
table.length
.Также может быть рассчитано с помощью
threshold
иloadFactor
, следовательно, не нужно определять как поле класса.Может получить эффективную мощность через:
capacity()
операции
Сначала найдите корзину по значению хеша, затем зацикливайте связанный список или ищите отсортированное дерево.
Сначала найдите корзину в соответствии с хэш-значением ключа.
Затем попробуйте найти значение:
При
threshold
достижении удваивает емкостьtable.length
хэш-таблицы ( ), затем выполняет повторное хеширование всех элементов, чтобы восстановить таблицу.Это может быть дорогой операцией.
Производительность
Время получения и сдачи сложность
O(1)
, потому что:O(1)
.O(1)
.O(1)
нетO(log N)
.источник
Хеш-код определяет, какую корзину для хэш-карты нужно проверить. Если в корзине находится более одного объекта, то выполняется линейный поиск, чтобы найти, какой элемент в корзине равен желаемому элементу (используя
equals()
) метод.Другими словами, если у вас есть идеальный хеш-код, тогда доступ к хеш-карте является постоянным, вам никогда не придется выполнять итерации по сегменту (технически вы также должны иметь сегменты MAX_INT, реализация Java может совместно использовать несколько хеш-кодов в одном сегменте для сократить требования к пространству). Если у вас худший хеш-код (всегда возвращает одно и то же число), то ваш доступ к хеш-карте становится линейным, поскольку вам нужно искать все элементы на карте (все они в одном ведре), чтобы получить то, что вы хотите.
В большинстве случаев хорошо написанный хеш-код не идеален, но достаточно уникален, чтобы предоставить вам более или менее постоянный доступ.
источник
Вы ошибаетесь в третьем пункте. Две записи могут иметь одинаковый хеш-код, но не быть равными. Посмотрите на реализацию HashMap.get из OpenJdk . Вы можете видеть, что он проверяет, что хэши равны, а ключи равны. Если бы пункт три был верным, то было бы ненужно проверять, чтобы ключи были равны. Хеш-код сравнивается перед ключом, потому что первый - более эффективное сравнение.
Если вы хотите узнать немного больше об этом, взгляните на статью в Википедии, посвященную разрешению коллизий Open Addressing , которая, как я считаю, является механизмом, который использует реализация OpenJdk. Этот механизм несколько отличается от подхода «корзины», который упоминается в других ответах.
источник
Итак, здесь мы видим, что если оба объекта S1 и S2 имеют разное содержимое, то мы почти уверены, что наш переопределенный метод Hashcode будет генерировать разные Hashcode (116232,11601) для обоих объектов. СЕЙЧАС, поскольку существуют разные хеш-коды, поэтому даже не стоит вызывать метод EQUALS. Потому что другой Hashcode ГАРАНТИРУЕТ РАЗНОЕ содержимое в объекте.
источник
Обновление Java 8 в HashMap-
Вы делаете эту операцию в своем коде -
Итак, предположим, что ваш хэш-код возвращается для обоих ключей
"old"
и"very-old"
одинаков. Тогда что будет.myHashMap
является HashMap, и предположим, что изначально вы не указали его емкость. Таким образом, емкость по умолчанию для java равна 16. Так что теперь, как только вы инициализировали hashmap с помощью нового ключевого слова, он создал 16 блоков. теперь, когда вы выполнили первое заявление-затем
"old"
вычисляется хеш- код для , и поскольку хеш-код тоже может быть очень большим целым числом, так, внутренне Java это сделал - (хеш-код здесь - хеш-код, а >>> - сдвиг вправо)так что для большей картины он вернет некоторый индекс, который будет между 0 и 15. Теперь ваша пара ключ-значение
"old"
и"old-value"
будет преобразовано в ключ и значение переменной экземпляра объекта занятие недвижимости с целью вступления во владение ею . и тогда этот объект записи будет сохранен в корзине, или вы можете сказать, что по определенному индексу этот объект записи будет сохранен.FYI-Entry - это класс в интерфейсе Map-Map.Entry, с этими сигнатурами / определениями
теперь, когда вы выполните следующую инструкцию -
и
"very-old"
дает тот же хеш-код"old"
, что и новая пара ключ-значение снова отправляется в тот же индекс или в тот же сегмент. Но так как это ведро не пустое, тоnext
переменная объекта Entry используется для хранения этой новой пары ключ-значение.и он будет сохранен как связанный список для каждого объекта, имеющего тот же хеш-код, но TRIEFY_THRESHOLD задается со значением 6. поэтому после достижения этого связанный список преобразуется в сбалансированное дерево (красно-черное дерево) с первым элементом в качестве корень.
источник
Каждый объект Entry представляет пару ключ-значение. Поле next относится к другому объекту Entry, если в ячейке более 1 записи.
Иногда может случиться, что хэш-коды для 2 разных объектов одинаковы. В этом случае 2 объекта будут сохранены в одном сегменте и будут представлены как LinkedList. Точкой входа является недавно добавленный объект. Этот объект ссылается на другой объект со следующим полем и так один. Последняя запись относится к нулю. Когда вы создаете HashMap с конструктором по умолчанию
Массив создается с размером 16 и балансировкой нагрузки 0,75 по умолчанию.
(Источник)
источник
Хеш-карта работает по принципу хэширования
Метод get (Key k) HashMap вызывает метод hashCode для объекта ключа и применяет возвращенное hashValue к своей собственной статической хэш-функции, чтобы найти местоположение сегмента (вспомогательный массив), где ключи и значения хранятся в форме вложенного класса с именем Entry (Map. Вступление). Итак, вы пришли к выводу, что из предыдущей строки и ключ, и значение хранятся в корзине как форма объекта Entry. Поэтому думать, что в корзине хранится только значение, не правильно и не произведет хорошего впечатления на интервьюера.
Если ключ имеет значение null, то ключи с нулевым значением всегда отображаются в хэш 0, то есть индекс 0.
Если ключ не равен нулю, он вызовет hashfunction для объекта ключа, см. Строку 4 в вышеупомянутом методе, т.е. key.hashCode (), поэтому после того, как key.hashCode () вернет hashValue, строка 4 выглядит следующим образом
и теперь он применяет возвращенное hashValue в свою собственную функцию хеширования.
Мы можем задаться вопросом, почему мы снова вычисляем hashvalue, используя hash (hashValue). Ответ защищает от хеш-функций низкого качества.
Теперь окончательное хеш-значение используется для определения местоположения сегмента, в котором хранится объект Entry. Объект ввода хранится в корзине следующим образом (хэш, ключ, значение, индекс ведра)
источник
Я не буду вдаваться в детали того, как работает HashMap, но приведу пример, чтобы мы могли помнить, как работает HashMap, связывая его с реальностью.
У нас есть ключ, значение, HashCode и ведро.
В течение некоторого времени мы будем связывать каждого из них со следующим:
Используя Map.get (ключ):
Стиви хочет попасть в дом своего друга (Джосс), который живет на вилле в VIP-обществе, пусть это будет Общество любителей Java. Адрес Джосса - это его SSN (он у всех разный). Поддерживается индекс, в котором мы узнаем название Общества на основе SSN. Этот индекс можно считать алгоритмом для определения HashCode.
Использование Map.put (ключ, значение)
Это находит подходящее общество для этого значения, находя HashCode, а затем значение сохраняется.
Я надеюсь, что это помогает, и это открыто для изменений.
источник
Ответ будет длинным, выпей и читай дальше ...
Хеширование - это сохранение пары ключ-значение в памяти, которая может быть прочитана и записана быстрее. Он хранит ключи в массиве и значения в LinkedList.
Скажем, я хочу сохранить 4 пары ключ-значение -
Поэтому для хранения ключей нам нужен массив из 4 элементов. Теперь, как мне сопоставить один из этих 4 ключей с 4 индексами массива (0,1,2,3)?
Таким образом, Java находит хэш-код отдельных ключей и сопоставляет их с определенным индексом массива. Hashcode Formulas - это
Хэш и девушка !! Я знаю, что вы думаете. Ваше увлечение этим диким дуэтом может заставить вас упустить важную вещь.
Почему ява умножает это на 31?
Теперь, как этот хэш-код отображается на индекс массива?
ответ
Hash Code % (Array length -1)
. Так“girl”
сопоставлено(3173020 % 3) = 1
в нашем случае. который является вторым элементом массива.и значение «ахан» сохраняется в LinkedList, связанном с индексом массива 1.
HashCollision - если вы попытаетесь найти
hasHCode
ключи“misused”
и“horsemints”
использовать формулы, описанные выше, вы увидите, что оба дают нам то же самое1069518484
. Воуаа !! урок выучен -Теперь хэш-карта выглядит так:
Теперь, если какое-то тело попытается найти значение для ключа
“horsemints”
, java быстро найдет его хеш-код, отредактирует его и начнет искать его значение в соответствующем LinkedListindex 1
. Таким образом, нам не нужно искать все 4 индекса массива, что ускоряет доступ к данным.Но, подождите, одну секунду есть 3 значения в этом LinkList, соответствующем индексу массива 1, как он узнает, какое из них было значением для ключевых «скачек»?
На самом деле я солгал, когда сказал, что HashMap просто хранит значения в LinkedList.
Он хранит обе пары ключ-значение в качестве записи карты. Так что на самом деле карта выглядит так.
Теперь вы можете видеть, проходя через связанный список, соответствующий ArrayIndex1, он фактически сравнивает ключ каждой записи с этим LinkedList с «скачками», и когда он находит его, он просто возвращает его значение.
Надеюсь, вам было весело читать это :)
источник
Как говорится, картинка стоит 1000 слов. Я говорю: какой-то код лучше, чем 1000 слов. Вот исходный код HashMap. Получить метод:
Таким образом, становится ясно, что хеш используется для поиска «корзины», и первый элемент всегда проверяется в этой корзине. Если нет, то
equals
ключ используется для поиска фактического элемента в связанном списке.Давайте посмотрим на
put()
метод:Это немного сложнее, но становится ясно, что новый элемент помещается во вкладку в позиции, рассчитанной на основе хеша:
i = (n - 1) & hash
вотi
индекс, куда будет помещен новый элемент (или это «корзина»).n
это размерtab
массива (массив «ведра»).Во-первых, его пытаются поставить в качестве первого элемента в этом «ведре». Если элемент уже существует, добавьте новый узел в список.
источник