Как внутренняя реализация 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, вероятно, будет более дорогой, требуя времени, пропорционального ее пропускной способности.