Устранение ConcurrentModificationException при итерации Map в Java

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Совершайте обход и удаление записей из Map с применением Iterator. Это безопасный способ, так как можно использовать метод Iterator.remove() в процессе обхода коллекции map.entrySet().iterator(), и избежать встречи с ConcurrentModificationException.

Java
Скопировать код
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 вместо текущего сравнения.

Кинга Идем в IT: пошаговый план для смены профессии

Усовершенствующая Java 8

Упрощаем с removeIf

Java 8 облегчила работу, предоставив метод removeIf для Map. Он прост, лаконичен и позволяет избежать ConcurrentModificationException.

Java
Скопировать код
map.entrySet().removeIf(entry -> entry.getValue() < 2);

Используя этот метод, вы можете забыть о необходимости вручную контролировать итератор. Жизнь стала легче, не правда ли?

Справляемся с многопоточностью

Если вы работаете с многопоточностью, рассмотрите использование ConcurrentHashMap или синхронизацию операций с вашей картой:

Java
Скопировать код
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.putAll(map);
// Отсутствуют блокирования!
concurrentMap.keySet().removeIf(key -> concurrentMap.get(key) < 2);

Это обеспечивает безопасное изменение карты даже при одновременной работе нескольких потоков.

Безопасность представлений коллекций

Методы values().removeAll и создание нового ArrayList из keySet() – это ваш способ избежать ConcurrentModificationException:

Java
Скопировать код
map.values().removeAll(Collections.singleton(valueToRemove));

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

Визуализация

Представьте, что каждый обход итератора – это процесс уборки. Вы проверяете каждую запись и удалите нежелательное (то, что соответствует вашему критерию).

Markdown
Скопировать код
Городской фестиваль (🏡🎈🎠 – Карта): {Касса: 120, Фуд-корт: 85, Карусель: 0, Колесо обозрения: 0}
Markdown
Скопировать код
Уборочная бригада (Iterable): Начинает с Кассы (Первая запись) и проверяет количество.
     Если стойка или аттракцион недостаточно популярны (Значение = 0), то они удаляются.
Markdown
Скопировать код
Старт: 🏡🎈[120] 🍔[85] 🎠[0] 🎡[0]
В процессе...
     Далее: 🎠 – Места пустуют. Отклонено 🚫
     Далее: 🎡 – Маловостребованные аттракционы не пропускаются 🚫
Чистый фестиваль: 🏡🎈[120] 🍔[85]

Итератор, словно уборочная команда, тщательно проверяет и удаляет записи с карты.

Использование лямбда-выражений и избегание проблем

Лямбда приходит на помощь!

Привлекательный Stream API из Java 8 может преобразовать ваши условия удаления в настоящее искусство читаемости:

Java
Скопировать код
map.keySet().removeIf(key -> someComplicatedCondition(key));

Замените someComplicatedCondition(key) на ваше собственное условие.

Предостережение

Помните, безопасность прежде всего! В данном случае нужно использовать Iterator.remove(). Никогда не вносите изменения в карту напрямую, чтобы не столкнуться с ConcurrentModificationException.

Универсальный инструмент для модификации карты

Удаление определённых значений

Требуется лишить карту определенного числа? Удалите его с использованием values().removeAll и Collections.singleton!

Java
Скопировать код
map.values().removeAll(Collections.singleton(valueToRemove));

Этот метод превращает вас в Таноса, безжалостно удаляющего нежелательные значения из Map.

Многопоточность требует внимания

В многопоточных условиях используйте блок synchronized, если работаете с коллекцией, не предназначенной для конкурентного использования. Это словно табличка «Не беспокоить!»:

Java
Скопировать код
synchronized (map) {
    Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        // Строгий дресс-код: цифры ниже '2' не разрешаются!
        if (iterator.next().getValue() < 2) iterator.remove();
    }
}

Данный подход позволяет временно иметь исключительное право на карту во время итерации и модификации, экономно сохраняя её от внешних вмешательств.