Что такое ConcurrentModificationException в Java и когда оно возникает?

«Что такое ConcurrentModificationException в Java и когда оно возникает?» — вопрос из категории Java Core, который задают на 22% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

ConcurrentModificationException — это runtime-исключение, которое возникает при некорректной модификации коллекции во время её итерации (кроме случаев, когда модификация выполняется через методы самого итератора). Это часть механизма fail-fast, предназначенного для предотвращения недетерминированного поведения.

Типичная причина (однопоточный случай):

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String item : list) { // Неявно использует Iterator
    if (item.equals("B")) {
        list.remove(item); // МОДИФИКАЦИЯ КОЛЛЕКЦИИ ВНЕ ИТЕРАТОРА -> ИСКЛЮЧЕНИЕ!
    }
}

Правильные способы удаления элементов во время итерации:

  1. Использовать Iterator.remove():
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        String item = iterator.next();
        if (item.equals("B")) {
            iterator.remove(); // Безопасное удаление через итератор
        }
    }
  2. Использовать методы коллекции с Java 8+:
    list.removeIf(item -> item.equals("B"));
  3. Собрать элементы для удаления и выполнить операцию после итерации.

Многопоточный контекст: Исключение также может быть выброшено, если один поток изменяет коллекцию, в то время как другой выполняет по ней итерацию. Для безопасной работы в многопоточной среде используйте:

  • ConcurrentHashMap, CopyOnWriteArrayList.
  • Явную синхронизацию блоков кода, работающих с коллекцией.