Я пытаюсь понять, как работает 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 выдает исключение? Спасибо




Это всего лишь одна из особенностей 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);
@LouisWasserman Я предполагаю, что myData.remove(key) является заполнителем для какой-то более сложной операции, которая может удалить записи. Конечно, если это можно перенаправить на iterator.remove(), тогда это работает нормально.
@MattTimmermans Спасибо, если я понимаю, что в примере ConcurrentHashMap с каждой итерацией мы удаляем ключ, там также обновляется набор ключей, но в HashMap нет обновления для набора ключей до конца цикла, поэтому есть исключение
@E2rabi keySet обновляется немедленно в обоих случаях, но в HashMap это обновление может испортить итератор, который использует цикл, поэтому итератор выдаст исключение при следующем использовании. В ConcurrentHashSet обновление никак не влияет на итератор.
@MattTimmermans Не совсем. Итератор имеет значение слабо последовательный,, что означает, что он «может (но [не гарантируется]) отражать любые изменения после построения».
@LouisWasserman Это правда, но в этом варианте использования однопоточный практически не так много свободы действий. В документе говорится, что итерация "отражает состояние хеш-таблицы в какой-то момент...", то есть вся итерация должна соответствовать всему состоянию хэш-таблицы в какой-то момент времени. После того, как вы увидели элемент в итерации, он может быть удален в любой момент в течение итерации, что делает согласованность с любым «моментом времени» после, что удаление намного сложнее — невозможно, если мы удаляем только те элементы, которые мы уже видел, как это делает ОП
Почему бы вам просто не использовать
Iterator.remove()вместо создания копии всего набора ключей? (Или очистить карту?)