Что делать, если HashMap заполнен?

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

  1. Предыдущая хэш-карта будет восстановлена ​​или все еще будет использоваться, если реконструкция произошла?
  2. так как нам нужна хеш-карта большего размера, будет ли изменена хэш-функция?
  3. Для ConcurrentHashMap, что, если один поток выполняет вставку (конечно, эта операция вставки привела к перестроению), а другой поток читает? Например, он будет читать из старой хэш-карты или из новой хэш-карты?

person wuchang    schedule 13.11.2014    source источник
comment
Какая-то конкретная причина, по которой вас беспокоит внутренняя работа этих классов? Сталкивались ли вы с проблемой, которая, по вашему мнению, может быть связана с этим?   -  person weston    schedule 13.11.2014


Ответы (4)


Предыдущая хэш-карта будет восстановлена ​​или все еще будет использоваться, если реконструкция произошла?

Это все та же хеш-карта, просто реконструируется внутреннее хранилище. После реконструкции старый массив бакетов больше не нужен и может быть собран.

Обновление: внутри HashMap есть Node<K,V>[] table. При изменении размера создается новый массив, элементы перемещаются, а затем table заменяется новым массивом. После этой операции сама карта больше не будет ссылаться на старый массив, поэтому, если нет других ссылок (что маловероятно из-за того, что table является частным пакетом), она подходит для gc.

так как нам нужна хеш-карта большего размера, будет ли изменена хэш-функция?

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

Обновление: HashMap вычисляет индекс корзины следующим образом: (size - 1) & hash, hash — это возвращаемое значение метода hashCode() ключа, которое не зависит от самой карты.

Для ConcurrentHashMap, что, если один поток выполняет вставку (конечно, эта операция вставки привела к перестроению), а другой поток читает? Например, он будет читать из старой хэш-карты или из новой хэш-карты?

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

Обновление: я бегло просмотрел исходники ConcurrentHashMap и обнаружил ссылки на текущий table, который используется get(), и возможный nextTable, который является целью для операций изменения размера. Во время изменения размера элементы переносятся в nextTable, а в конце table устанавливаются в nextTable, эффективно переключая таблицы.

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

Документация также намекает на это:

Хэш-таблица, поддерживающая полный параллелизм извлечения и высокий ожидаемый параллелизм для обновлений.

Это означает, что get не будет блокироваться, но put, remove и т. д. могут заблокироваться в какой-то момент.

person Thomas    schedule 13.11.2014

Из HashMap API

1)

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

2) Если вы заранее знаете размер, лучше создавайте хэшбакеты при построении объекта карты.

HashMap(int initialCapacity)
person csarathe    schedule 13.11.2014

Предыдущая хэш-карта будет восстановлена ​​или все еще будет использоваться, если реконструкция произошла?

Если нет ссылок на предыдущие структуры, они могут быть удалены сборщиком мусора.

так как нам нужна хеш-карта большего размера, будет ли изменена хэш-функция?

Не совсем. Хэш остался прежним, изменилось только то, как хэши сопоставляются с сегментами. Например, до расширения хэши 27 103 и 32 504 могут сопоставляться с одним и тем же сегментом. После расширения они могут отображаться в разных корзинах. Хэш-карта будет иметь больше сегментов и меньше элементов в каждом сегменте.

Для ConcurrentHashMap, что, если один поток вставляет (конечно, эта операция вставки привела к повторной конструкции), а другой поток читает? Например, он будет читать из старой хэш-карты или из новой хэш-карты?

Если вам не все равно, то вы должны использовать замок. Вы используете ConcurrentHashMap, потому что вам нужен параллелизм. Если вам нужен предсказуемый порядок операций чтения и записи, вы должны их упорядочить. Это верно даже при отсутствии реконструкции. Если вы выполняете чтение и запись одновременно, чтение может отражать или не отражать запись. Если вы выполняете запись в одном потоке и выполняете чтение из другого потока до того, как эта запись вернется, чтение может отражать или не отражать запись. ConcurrentHashMap дает вам разумные результаты, но не обеспечивает упорядочение (как это могло быть?).

person David Schwartz    schedule 13.11.2014

  1. предыдущий будет собираться мусором
  2. хеш-функция не изменится, но хеш-значения (сегменты) могут измениться, потому что хеш-функция обычно зависит от размера хэш-карты.
  3. хэш-карты не являются потокобезопасными. Используйте ConcurrentHashMap, если хэш-карта является общей для потоков.
person abhi5306    schedule 13.11.2014