Запутался в разнице между поведением ConcurrentHashMap и HashMap в этом примере

Я пытаюсь понять, как работает ConcurrentHashMap. Я нашел пример, но я не могу его понять. Вот его код:

Map<String, Object> myData = new HashMap<String, Object>();
myData.put("A", 1);
myData.put("B", 2);
for (String key : myData.keySet()) {
    myData.remove(key);
}

Это вызовет исключение ConcurrentModificationException во время выполнения.

Однако этот код с использованием ConcurrentHashMap будет работать правильно:

Map<String, Object> myData = new ConcurrentHashMap<String, Object>();
myData.put("A", 1);
myData.put("B", 2);
for (String key : myData.keySet()) }
    myData.remove(key);
}

Может ли кто-нибудь объяснить мне, почему ConcurrentHashMap позволяет удалять ключи, а HashMap выдает исключение? Спасибо

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
0
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это всего лишь одна из особенностей ConcurrentHashMap. Цитата из документов:

Similarly, Iterators, Spliterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration. They do not throw ConcurrentModificationException.

Однако ConcurrentHashMap на самом деле не поддерживает ваш вариант использования. Это сделано для того, чтобы итерация в одном потоке происходила одновременно с модификациями, сделанными в других потоках.

Если это ваша единственная причина использовать ConcurrentHashMap, то вам, вероятно, следует пересмотреть свое решение, потому что это намного дороже, чем HashMap. Вам лучше просто сделать копию набора ключей, прежде чем использовать его следующим образом:

Map<String, Object> myData = new HashMap<String, Object>();
myData.put("A", 1);
myData.put("B", 2);
for(String key: myData.keySet().toArray(new String[0]))
    myData.remove(key); 

Почему бы вам просто не использовать Iterator.remove() вместо создания копии всего набора ключей? (Или очистить карту?)

Louis Wasserman 18.02.2019 22:39

@LouisWasserman Я предполагаю, что myData.remove(key) является заполнителем для какой-то более сложной операции, которая может удалить записи. Конечно, если это можно перенаправить на iterator.remove(), тогда это работает нормально.

Matt Timmermans 18.02.2019 22:41

@MattTimmermans Спасибо, если я понимаю, что в примере ConcurrentHashMap с каждой итерацией мы удаляем ключ, там также обновляется набор ключей, но в HashMap нет обновления для набора ключей до конца цикла, поэтому есть исключение

e2rabi 19.02.2019 11:07

@E2rabi keySet обновляется немедленно в обоих случаях, но в HashMap это обновление может испортить итератор, который использует цикл, поэтому итератор выдаст исключение при следующем использовании. В ConcurrentHashSet обновление никак не влияет на итератор.

Matt Timmermans 19.02.2019 14:57

@MattTimmermans Не совсем. Итератор имеет значение слабо последовательный,, что означает, что он «может (но [не гарантируется]) отражать любые изменения после построения».

Louis Wasserman 20.02.2019 00:10

@LouisWasserman Это правда, но в этом варианте использования однопоточный практически не так много свободы действий. В документе говорится, что итерация "отражает состояние хеш-таблицы в какой-то момент...", то есть вся итерация должна соответствовать всему состоянию хэш-таблицы в какой-то момент времени. После того, как вы увидели элемент в итерации, он может быть удален в любой момент в течение итерации, что делает согласованность с любым «моментом времени» после, что удаление намного сложнее — невозможно, если мы удаляем только те элементы, которые мы уже видел, как это делает ОП

Matt Timmermans 20.02.2019 03:56

Другие вопросы по теме