Как внутренняя реализация LinkedHashMap отличается от реализации HashMap?
Я прочитал, что HashMap имеет следующую реализацию:
main array
↓
[Entry] → Entry → Entry ← linked-list implementation
[Entry]
[Entry] → Entry
[Entry]
[null ]
Итак, у него есть массив объектов Entry.
Вопросы:
-
Мне было интересно, как индекс этого массива может хранить несколько объектов Entry в случае одного и того же hashCode, но разных объектов.
-
Как это отличается от реализации LinkedHashMap
? Его двусвязная реализация списка карт, но поддерживает ли она такой массив, как выше, и как он хранит указатели на следующий и предыдущий элемент?
Ответы
Ответ 1
Таким образом, он имеет массив объектов Entry
.
Не совсем. Он имеет массив цепей объектов Entry
. Объект HashMap.Entry
имеет поле next
, позволяющее объектам Entry
быть скованным как связанный список.
Мне было интересно, как индекс этого массива может хранить несколько объектов Entry
в случае одного и того же hashCode, но разных объектов.
Потому что объекты Entry
привязаны. См. Выше. (И "картина" в вашем Вопросе!)
Как это отличается от реализации LinkedHashMap
? Его двунаправленная реализация списка карт, но поддерживает ли он такой массив, как выше, и как он хранит указатели на следующий и предыдущий элемент?
В реализации LinkedHashMap
класс LinkedHashMap.Entry
расширяет класс HashMap.Entry
, добавляя поля before
и after
. Эти поля используются для сборки объектов LinkedHashMap.Entry
в независимый дважды связанный список, который записывает порядок вставки. Итак, в классе LinkedHashMap
объекты записи находятся в двух разных цепочках:
-
односвязная цепочка хэширования, доступ к которой осуществляется через основной массив хэшей, и
-
отдельный двусвязный список всех записей, которые хранятся в порядке ввода.
Ответ 2
HashMap не поддерживает порядок вставки, поэтому не поддерживает ни одного дважды связанного списка.
Наиболее характерной особенностью LinkedHashMap является то, что он поддерживает порядок вставки пар ключ-значение. Для этого LinkedHashMap использует двойной Linked List.
Запись LinkedHashMap выглядит так:
static class Entry<K, V> {
K key;
V value;
Entry<K,V> next;
Entry<K,V> before, after; //For maintaining insertion order
public Entry(K key, V value, Entry<K,V> next){
this.key = key;
this.value = value;
this.next = next;
}
}
Используя до и после - мы отслеживаем только что добавленную запись в LinkedHashMap, которая помогает нам поддерживать порядок вставки.
Прежде чем обращаться к предыдущей записи и
после ссылается на следующую запись в LinkedHashMap.
![LinkedHashMap]()
Для диаграмм и пошаговых объяснений см. http://www.javamadesoeasy.com/2015/02/linkedhashmap-custom-implementation.html
Спасибо..!!
Ответ 3
Возьмите взгляд для себя. Для справок в будущем вы можете просто google:
java Источник LinkedHashMap
HashMap
использует LinkedList
для обработки коллизий, но разница между HashMap
и LinkedHashMap
заключается в том, что LinkedHashMap
имеет предикативный порядок итерации, который достигается посредством дополнительного двусвязного списка, который обычно поддерживает порядок вставки ключей. Исключение составляет момент, когда ключ повторно вставлен, и в этом случае он возвращается к исходной позиции в списке.
Для справки, повторение с помощью LinkedHashMap
более эффективно, чем повторение с помощью HashMap
, но LinkedHashMap
менее эффективно.
В случае, если из моего приведенного выше объяснения не ясно, процесс хеширования одинаков, поэтому вы получаете преимущества обычного хеша, но вы также получаете преимущества итерации, как указано выше, поскольку вы используете вдвойне связанный список для поддержания порядка ваших объектов Entry
, который не зависит от связанного списка, используемого во время хэширования для коллизий, в случае, если это было неоднозначно.
EDIT: (в ответ на комментарий OP):
A HashMap
поддерживается массивом, в котором некоторые слоты содержат цепочки объектов Entry
для обработки столкновений. Чтобы перебрать все пары (ключ, значение), вам нужно будет пройти через все слоты в массиве, а затем пройти через LinkedLists
; следовательно, ваше общее время будет пропорционально емкости.
При использовании LinkedHashMap
все, что вам нужно сделать, это пройти через дважды связанный список, поэтому общее время пропорционально размеру.
Ответ 4
hashCode
будет отображаться в любом ведре хеш-функцией. Если в hashCode
есть столкновение, чем HashMap
, устраните это столкновение путем цепочки, то есть оно добавит значение в связанный список. Ниже приведен код, который делает это:
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
392 Object k;
393 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
394 `enter code here` V oldValue = e.value;
395 e.value = value;
396 e.recordAccess(this);
397 return oldValue;
398 }
399 }
Вы можете четко видеть, что он пересекает связанный список, и если он находит ключ, он заменяет старое значение новым добавлением в связанный список.
Но разница между LinkedHashMap
и HashMap
равна LinkedHashMap
, которая поддерживает порядок вставки. Из docs:
Этот связанный список определяет порядок итераций, который обычно является порядком, в котором ключи были вставлены в карту (порядок вставки). Обратите внимание, что порядок вставки не изменяется, если ключ повторно вставлен в карту. (Ключ k повторно вводится в карту m, если m.put(k, v) вызывается, когда m.containsKey(k) возвращает true непосредственно перед вызовом).
Ответ 5
Поскольку ни один из других ответов на самом деле не объясняет, как что-то подобное можно реализовать, я дам ему шанс.
Один из способов состоял бы в том, чтобы некоторая дополнительная информация в значении (пары ключ- > значение) не была видима пользователю, которая имела ссылку на предыдущий и следующий элементы, вставленные в хэш-карту. Преимущества в том, что вы по-прежнему можете удалять элементы в постоянном времени. Удаление из хэш-карты - это постоянное время, и удаление из связанного списка в этом случае происходит потому, что у вас есть ссылка на запись. Вы можете по-прежнему вставлять в постоянное время, поскольку вставка карты хеш-памяти постоянна, связанный список обычно не выполняется, но в этом случае у вас есть постоянный доступ времени к месту в связанном списке, поэтому вы можете вставлять его в постоянное время, и, наконец, поиск является постоянным временем потому что вам нужно иметь дело с частью хэш-карты для структуры.
Имейте в виду, что структура данных, подобная этой, не приходит без затрат. Размер хэш-карты значительно возрастет из-за всех дополнительных ссылок. Каждый из основных методов будет немного медленнее (может иметь значение, если они вызываются повторно). И косвенность структуры данных (не уверен, что этот реальный термин: P) увеличивается, хотя это может быть не так дорого, потому что ссылки гарантированно указывают на вещи внутри хэш-карты.
Поскольку единственным преимуществом такого типа структуры является то, что он сохраняет порядок, будьте осторожны, когда вы его используете. Также, читая ответ, помните, я не знаю, что так оно и было реализовано, но именно так я бы сделал это, если задал задачу.
В oracle docs есть цитата, подтверждающая некоторые из моих догадок.
Эта реализация отличается от HashMap тем, что она поддерживает список с двойной связью, проходящий через все его записи.
Другая соответствующая цитата с того же сайта.
Этот класс предоставляет все необязательные операции с Картой и разрешает нулевые элементы. Как и HashMap, он обеспечивает постоянную производительность для основных операций (добавлять, содержать и удалять), предполагая, что хеш-функция правильно распределяет элементы среди ведер. Производительность, вероятно, будет чуть ниже, чем у HashMap, из-за дополнительных затрат на поддержание связанного списка, за одним исключением: Итерация над представлениями коллекции LinkedHashMap требует времени, пропорционального размеру карты, независимо от ее емкости, Итерация над HashMap, вероятно, будет более дорогой, требуя времени, пропорционального ее пропускной способности.