Может ли ConcurrentModificationException возникнуть в однопоточном коде и почему?

ConcurrentModificationException сообщает о некорректном изменении коллекции во время итерации независимо от числа потоков; поэтому однопоточная модификация с нарушением правил итерации — частая причина.

Короткий ответ

Что ответить на собеседовании

ConcurrentModificationException сообщает о некорректном изменении коллекции во время итерации независимо от числа потоков; поэтому однопоточная модификация с нарушением правил итерации — частая причина.

Подробный разбор

Ответ с пояснениями

ConcurrentModificationException в однопоточном коде: бывает ли такое?

  • область: Java коллекции, исключения
  • ConcurrentModificationException (CME) — исключение, возникающее, когда во время итерации обнаруживается параллельное изменение коллекции
  • В однопоточном коде CME может появиться, если:
  • одновременно применяются методы итератора и выполняется прямая модификация коллекции (например, remove/clear вне итератора)
  • итератор вызывается после изменения коллекции другим методом без reset итератора
  • CME не указывает однозначно на наличие нескольких потоков: его причина — структурное изменение коллекции во время итерации
  • часто исключение возникает из-за вызова методов, меняющих коллекцию, не через итератор внутри цикла
  • для предотвращения следует применять методы итератора при удалении/изменении либо итерироваться по копии коллекции
  • практический пример: for (Element e : list) { list.remove(e); } вызывает CME даже при выполнении в одном потоке

Итого: ConcurrentModificationException сообщает о некорректном изменении коллекции во время итерации независимо от числа потоков; поэтому однопоточная модификация с нарушением правил итерации — частая причина.

Подробный ответ

Основной ответ

ConcurrentModificationException (CME) — исключение, которое обычно появляется при структурном изменении коллекции во время её обхода с помощью fail-fast итератора в Java. Хотя в классическом представлении CME связывают с конкурентным доступом нескольких потоков, такое исключение возможно и в однопоточном коде. Например, цикл может перебирать коллекцию, а его тело — напрямую изменять её, не используя итератор.

Ключевые моменты

  • Модификация в одном потоке во время итерации: если коллекция перебирается через for-each или явный итератор, а внутри цикла вызываются методы, меняющие её размер (add(), remove()), итератор обнаруживает несовпадение числа модификаций и выбрасывает CME.
  • Fail-fast итераторы: коллекции из java.util, включая ArrayList и HashSet, используют fail-fast итераторы. Они контролируют число изменений коллекции с помощью поля modCount. Если при вызове next() значение этой переменной не совпадает, возникает исключение.
  • Причина заключается не в многопоточности как таковой, а в структурном изменении коллекции во время обхода: даже один поток вызовет CME, если логика неосторожно изменяет коллекцию, например меняет список в цикле без применения метода итератора remove().

Практический контекст

В реальных проектах CME нередко появляется из-за неправильной работы с коллекциями в однопоточном коде, особенно в сложных циклах, где коллекция изменяется во время обхода. Оптимальный вариант — использовать итератор и его методы либо создать копию коллекции для безопасной итерации. Для многопоточных сценариев применяют конкурентные коллекции из java.util.concurrent, например CopyOnWriteArrayList, либо синхронизацию.

Таким образом, ConcurrentModificationException возможен и в однопоточном коде: исключение указывает на некорректное изменение коллекции в процессе итерации, а не само по себе на параллельное выполнение.

Практика в реальном времени

Подготовьтесь к следующему собеседованию

Interview Boost учитывает вакансию, резюме и технологии и помогает сформулировать ответ прямо во время интервью.

Начать подготовку