Устраняем ConcurrentModificationException в HashMap Java
Пройдите тест, узнайте какой профессии подходите
Быстрый ответ
// Для корректного удаления элементов применяйте метод remove() у итератора
for (Iterator<Map.Entry<KeyType, ValueType>> it = hashMap.entrySet().iterator(); it.hasNext();) {
// Удаление следует выполнять осторожно, предварительно проверив условие для удаления элемента
if (shouldRemove(it.next().getKey())) it.remove();
}
Для безопасной итерации по HashMap
с последующим удалением элементов без возникновения ConcurrentModificationException
, применяйте Iterator
из метода entrySet()
. Вызывайте it.remove()
сразу после it.next()
.
Подробное объяснение
Использование Iterator для управляемого и последовательного удаления
Изменение содержимого HashMap
в процессе его итерации может вызвать ConcurrentModificationException
. Именно тут Iterator
предлагает надёжный метод просмотра и удаления элементов по мере перебора.
// Пройдясь по "площади" HashMap, не забывайте о корректном удалении элементов!
Iterator<Map.Entry<KeyType, ValueType>> it = hashMap.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<KeyType, ValueType> entry = it.next();
// Внимательность при удалении избавит вас от головной боли в процессе отладки!
if (shouldRemove(entry.getKey())) it.remove();
}
Использование removeIf и лямбда-выражений
С появлением Java 8 наш код начал смотреться проще и стал интуитивно понятнее. Метод removeIf
в сочетании с лямбда-выражениями позволяет удалять элементы, основываясь на предикате, быстро и лаконично:
// Очищаем hashMap от значений, которые не учитывают регистр и равны "Sample"
hashMap.entrySet().removeIf(entry -> "Sample".equalsIgnoreCase(entry.getValue()));
Визуализация
Представьте процесс итерации и удаления элементов как проход по ряду домино, где вы, по собственному выбору, опрокидываете отдельные костяшки, не запуская цепную реакцию:
| Ряд домино | Действие |
| -------------- | --------- |
| 🟨🟦🟥🟩🟪 | Старт |
| 🟨🚶♂️🟥🟩🟪 | Итерация |
| 🟨❌🟥🟩🟪 | Отметка |
| 🟨🟥🟩🟪 | Удаление |
Асинхронное удаление элементов может запустить домино-эффект:
| Итерация | Синхронизация изменений |
| ---------------------- | ---------------------- |
| 🚶♂️ (🟦) 🟥🟩🟪 | ✅ Удаление без проблем |
| 🚶♂️ (Удалить 🟥 / ❌) 🟩🟪 | 🚫 Цепная реакция! |
Iterator
защищает вас от таких нежданных ситуаций:
Iterator<Map.Entry<Key, Value>> it = hashMap.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<Key, Value> entry = it.next();
if(shouldRemove(entry.getKey())) {
it.remove(); // 🛠️ Удаляем эломент, не разрушая порядок в ряду
}
}
Эффективное удаление с использованием removeIf и лямбда-выражений
Лямбда-выражения, появившиеся в Java 8, существенно упростили процесс удаления неподходящих элементов. Вы можете проверять условия в любом месте итерации, что обеспечивает более элегантный и фокусированный код:
// Одним движением удаляем непригодные элементы
hashMap.entrySet().removeIf(entry -> entry.getKey().hasBadSmell());
Итерация и фильтрация с помощью потоков в Java
Потоки в Java 8 предназначены для тех, кто предпочитает функциональный подход к программированию. Они обеспечивают удобство фильтрации и сбора элементов, исключая нежелательные ключи:
// Гэндальф манипуляции с ненужными ключами: "Вам сюда не пройти!"
hashMap = hashMap.entrySet().stream()
.filter(entry -> !unwantedKeys.contains(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Заметим, что это создаст новый HashMap
, что может быть не наилучшим решением для большого объема данных.
Полезные материалы
- Iterator (Java Platform SE 8) — Изучите метод
Iterator.remove()
для лучшего понимания. - Calling remove in foreach loop in Java – Stack Overflow — Нужны примеры? Обсуждение
Iterator.remove()
на Stack Overflow — ваш источник информации. - ConcurrentModificationException (Java Platform SE 7) — Изучите основные проблемы, чтобы избежать ошибок при модификации коллекции «на ходу».
- Java Practices->Ways of iterating — Разнообразие способов итерации — это хорошо! Руководство по различным методам итерации по коллекциям в Java.
- Why is a ConcurrentModificationException thrown and how to debug it – Stack Overflow — Разъяснение ConcurrentModificationException поможет вам избежать неожиданных ошибок в процессе отладки.
- Java – The HashMap Class — Знание HashMap — залог успешного программирования! Подробный урок об использовании
HashMap
в Java. - Map.Entry interface in Java with example – GeeksforGeeks — Познакомьтесь с интерфейсом Map.Entry для удобства итерации и модификации map.