Какое исключение выбрасывается при изменении коллекции во время итерации по ней в Java?

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

Ответ

При изменении коллекции во время итерации по ней (не через методы итератора) выбрасывается ConcurrentModificationException.

Механизм: Это часть контракта fail-fast итераторов. Итератор проверяет, что внутренний счетчик модификаций коллекции (modCount) не изменился с момента его создания. Любое структурное изменение коллекции (добавление, удаление) увеличивает этот счетчик. Если итератор обнаруживает расхождение, он немедленно выбрасывает исключение.

Пример возникновения:

List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);

for (Integer num : numbers) { // Неявно создается итератор
    if (num == 1) {
        numbers.add(3); // Прямое изменение коллекции -> Исключение!
    }
}

Способы безопасного изменения во время итерации:

  1. Использовать Iterator.remove():
    Iterator<Integer> it = numbers.iterator();
    while (it.hasNext()) {
        Integer num = it.next();
        if (num == 1) {
            it.remove(); // Безопасно
        }
    }
  2. Создать копию коллекции для итерации: Итерироваться по копии, изменять оригинал.
  3. Использовать CopyOnWriteArrayList: Эта коллекция создает новую копию внутреннего массива при каждой модификации, что позволяет безопасно итерироваться по старой копии.