Как пройтись циклом по структуре данных и добавить элементы если нужно
Что проверяют
Знаете ли вы про fail-fast итераторы и ConcurrentModificationException и умеете ли выбрать способ изменения коллекции под задачу. Следом обычно спрашивают, как сделать то же самое с Map или в многопоточном коде.
Как отвечать
Сначала объясните причину. У ArrayList, HashMap и других коллекций из java.util есть счётчик изменений modCount. Итератор запоминает его при создании и сверяет на каждом next(). Добавили элемент мимо итератора, счётчик разошёлся, следующий next() бросает исключение. Это происходит и в одном потоке, многопоточность здесь ни при чём.
Дальше варианты:
ListIterator.add(e): вставляет элемент перед курсором, и текущий обход его не посетит. Есть только уList, у обычногоIteratorметодаaddнет, толькоremove;- отдельная коллекция: новое собирается в
toAdd, после циклаlist.addAll(toAdd). Самый читаемый вариант, а дляMapпочти единственный:putAllпосле обхода; - цикл по индексу:
for (int i = 0; i < list.size(); i++)проверяет размер на каждой итерации, поэтому добавленные элементы тоже обработаются. Иногда это и нужно, но легко получить бесконечный цикл. Если новое обрабатывать не надо, зафиксируйте размер до цикла; - очередь: если новые элементы тоже должны пройти обработку (обход графа, раскрытие вложенных задач), берите
ArrayDequeи циклwhile (!queue.isEmpty())сpoll()иoffer(); CopyOnWriteArrayList: итератор идёт по снимку, исключения нет, но каждая запись копирует весь массив. Подходит для редких изменений при частом чтении из разных потоков.
В стримах источник менять нельзя вовсе, результат собирается в новую коллекцию через flatMap и toList().
Где спотыкаются
Говорят «добавлю через итератор», имея в виду Iterator. Добавление есть только у ListIterator.
Считают исключение гарантией. Документация называет fail-fast поведение best effort: например, удаление предпоследнего элемента в for-each завершает цикл молча, и последний элемент просто не проверяется.
Лечат всё через CopyOnWriteArrayList или Collections.synchronizedList. Первый дорог на записи, второй от этого исключения вообще не спасает.