filterTripleA ломается на одной строчке ConcurrentModificationException
Задача звучит просто: удалить из переданной коллекции все строки, начинающиеся на "aaa", не сломав обход. Сигнатура — public static void filterTripleA(Collection strings).
Первое, что пишет кандидат на автомате — for-each с remove внутри:
for (String s : strings) { if (s.startsWith("aaa")) { strings.remove(s); // бросит ConcurrentModificationException } }
for-each под капотом использует Iterator, а modCount коллекции меняется при любом внешнем remove. Итератор на следующем hasNext() сверяет modCount и бросает исключение. Работает не всегда: если удаляемый элемент — предпоследний, цикл может завершиться без исключения, просто пропустив часть удалений. Это хуже, чем стабильный краш — баг воспроизводится через раз.
Правильный путь — через сам Iterator и его remove():
public static void filterTripleA(Collection strings) { Iterator it = strings.iterator(); while (it.hasNext()) { if (it.next().startsWith("aaa")) { it.remove(); } } }
Iterator.remove() знает про текущую позицию курсора и корректно обновляет внутреннее состояние, не трогая modCount способом, который ломает следующий hasNext().
Следом спросят: а что если strings — это List, можно ли использовать removeIf? Да, Collection.removeIf(Predicate) делает то же самое за один вызов и читается короче: strings.removeIf(s -> s.startsWith("aaa")). Но если на собесе просят написать руками через Iterator — это проверка понимания, а не лени.
remove внутри for-each — не баг компилятора, а ожидаемое поведение контракта Iterator, который без исключения ловится не всегда.
Тренажёр: 900+ вопросов и задач, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки