Устранение ConcurrentModificationException при итерации Map в Java
Быстрый ответ
Совершайте обход и удаление записей из Map
с применением Iterator
. Это безопасный способ, так как можно использовать метод Iterator.remove()
в процессе обхода коллекции map.entrySet().iterator()
, и избежать встречи с ConcurrentModificationException
.
Map<String, Integer> map = new HashMap<>();
map.put("a", 1); map.put("b", 2);
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
// Мы на диете – без лишних калорий.
if (it.next().getValue() < 2) it.remove();
}
Подставьте собственное условие удаления в оператор if
вместо текущего сравнения.
Усовершенствующая Java 8
Упрощаем с removeIf
Java 8 облегчила работу, предоставив метод removeIf
для Map
. Он прост, лаконичен и позволяет избежать ConcurrentModificationException
.
map.entrySet().removeIf(entry -> entry.getValue() < 2);
Используя этот метод, вы можете забыть о необходимости вручную контролировать итератор. Жизнь стала легче, не правда ли?
Справляемся с многопоточностью
Если вы работаете с многопоточностью, рассмотрите использование ConcurrentHashMap
или синхронизацию операций с вашей картой:
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.putAll(map);
// Отсутствуют блокирования!
concurrentMap.keySet().removeIf(key -> concurrentMap.get(key) < 2);
Это обеспечивает безопасное изменение карты даже при одновременной работе нескольких потоков.
Безопасность представлений коллекций
Методы values().removeAll
и создание нового ArrayList
из keySet()
– это ваш способ избежать ConcurrentModificationException
:
map.values().removeAll(Collections.singleton(valueToRemove));
Этот подход позволяет осуществить изменения по обе стороны и избежать проблем с исключениями.
Визуализация
Представьте, что каждый обход итератора – это процесс уборки. Вы проверяете каждую запись и удалите нежелательное (то, что соответствует вашему критерию).
Городской фестиваль (🏡🎈🎠 – Карта): {Касса: 120, Фуд-корт: 85, Карусель: 0, Колесо обозрения: 0}
Уборочная бригада (Iterable): Начинает с Кассы (Первая запись) и проверяет количество.
Если стойка или аттракцион недостаточно популярны (Значение = 0), то они удаляются.
Старт: 🏡🎈[120] 🍔[85] 🎠[0] 🎡[0]
В процессе...
Далее: 🎠 – Места пустуют. Отклонено 🚫
Далее: 🎡 – Маловостребованные аттракционы не пропускаются 🚫
Чистый фестиваль: 🏡🎈[120] 🍔[85]
Итератор, словно уборочная команда, тщательно проверяет и удаляет записи с карты.
Использование лямбда-выражений и избегание проблем
Лямбда приходит на помощь!
Привлекательный Stream
API из Java 8 может преобразовать ваши условия удаления в настоящее искусство читаемости:
map.keySet().removeIf(key -> someComplicatedCondition(key));
Замените someComplicatedCondition(key)
на ваше собственное условие.
Предостережение
Помните, безопасность прежде всего! В данном случае нужно использовать Iterator.remove()
. Никогда не вносите изменения в карту напрямую, чтобы не столкнуться с ConcurrentModificationException
.
Универсальный инструмент для модификации карты
Удаление определённых значений
Требуется лишить карту определенного числа? Удалите его с использованием values().removeAll
и Collections.singleton
!
map.values().removeAll(Collections.singleton(valueToRemove));
Этот метод превращает вас в Таноса, безжалостно удаляющего нежелательные значения из Map
.
Многопоточность требует внимания
В многопоточных условиях используйте блок synchronized
, если работаете с коллекцией, не предназначенной для конкурентного использования. Это словно табличка «Не беспокоить!»:
synchronized (map) {
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
// Строгий дресс-код: цифры ниже '2' не разрешаются!
if (iterator.next().getValue() < 2) iterator.remove();
}
}
Данный подход позволяет временно иметь исключительное право на карту во время итерации и модификации, экономно сохраняя её от внешних вмешательств.