Ответ
При изменении коллекции во время итерации по ней (не через методы итератора) выбрасывается 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); // Прямое изменение коллекции -> Исключение!
}
}
Способы безопасного изменения во время итерации:
- Использовать
Iterator.remove():Iterator<Integer> it = numbers.iterator(); while (it.hasNext()) { Integer num = it.next(); if (num == 1) { it.remove(); // Безопасно } } - Создать копию коллекции для итерации: Итерироваться по копии, изменять оригинал.
- Использовать
CopyOnWriteArrayList: Эта коллекция создает новую копию внутреннего массива при каждой модификации, что позволяет безопасно итерироваться по старой копии.
Ответ 18+ 🔞
А, слушай, вот эта ваша ConcurrentModificationException — это вообще классика, пиздец! Прямо как в анекдоте: "Нельзя так делать, но если очень хочется, то можно, но потом охуеешь".
Представь картину, блядь. Ты спокойно идёшь по списку, как по магазину, смотришь на элементы. А тут твоя же рука, ёпта, из другого кармана, начинает в этот самый список хуярить новые штуки или выкидывать старые. Коллекция, сука, в шоке: "Мужик, ты чё, блядь? Я уже считаюсь, а ты тут меня ломаешь на ходу!" И бац — тебе в ебальник летит ConcurrentModificationException. Это как будто тебе говорят: "Чувак, блядь, определись — либо ты меня перебираешь, либо меняешь. Одновременно — нихуя!"
Вот смотри, самый идиотский пример, который все делают:
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
for (Integer num : numbers) { // Тут внутри невидимый итератор уже создался, блядь
if (num == 1) {
numbers.add(3); // АХТУНГ! Пиздец! Прямое вмешательство в процесс!
}
}
Запустишь — получишь по ебалу исключением. Итератор, сука, как сторожевой пёс: он запомнил, сколько раз коллекцию трогали (modCount), а тут бац — количество изменилось без его ведома. Он такой: "Нахуй! Измена! Всё, я сваливаю!" — и кидает тебе ошибку.
Так как же, блядь, делать-то правильно, если очень надо?
-
Через самого итератора. У него есть свой законный метод
remove(). Это как сказать: "Слушай, я сейчас через тебя работаю, вот этот элемент — нахуй его".Iterator<Integer> it = numbers.iterator(); while (it.hasNext()) { Integer num = it.next(); if (num == 1) { it.remove(); // Всё легально, итератор сам всё согласует } }С
addчерез обычныйIteratorсложнее, его там нет. Для этого уже другие итераторы нужны. -
Сделать копию, блядь. Идея проще некуда: итерируешься по одной копии коллекции, а хуяришь в другую (или в оригинал). Они же разные, сука, объекты — итератору на оригинал похуй, что ты творишь с копией.
-
Взять
CopyOnWriteArrayList. Это, блядь, специальная коллекция для таких распиздяев, которые любят менять на лету. Её принцип — "пиши, сука, в копию". Когда ты её меняешь, она создаёт полную новую копию внутреннего массива. А старый итератор работает со старой, неизменной копией. Удобно, но дорого по памяти, если изменения частые — копии будут лететь, как хуи с горы.
Короче, суть в чём: Java тебя страхует от самого себя, от кривых race condition в одном потоке. Не лезь, блядь, с ногами в тазик, пока в нём стираешь. Либо стирай, либо ноги мой. Или купи два тазика, ёпта.