Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Как обойти ConcurrentModificationException при модификации коллекции
Одна из самых частых проблем в Java - попытка модифицировать коллекцию (добавлять/удалять элементы) во время итерации по ней. Это вызывает ConcurrentModificationException.
Проблема
// ✗ Вызывает ConcurrentModificationException
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String item : list) {
if (item.equals("B")) {
list.remove(item); // Ошибка!
}
}
Решение 1: Iterator.remove()
// ✓ Правильный способ удаления во время итерации
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (item.equals("B")) {
iterator.remove(); // Безопасно!
}
}
// Результат: [A, C]
Это самый безопасный способ, так как Iterator знает о модификации.
Решение 2: removeIf() (Java 8+)
// ✓ Современный функциональный способ
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
list.removeIf(item -> item.equals("B"));
// Результат: [A, C]
Лаконично и безопасно. Работает со всеми коллекциями.
Решение 3: Stream API
// ✓ Функциональный подход с потоками
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> filtered = list.stream()
.filter(item -> !item.equals("B"))
.collect(Collectors.toList());
// filtered: [A, C]
Не модифицирует исходный список, создает новый.
Решение 4: Копирование коллекции
// ✓ Итерируем по копии, модифицируем оригинал
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String item : new ArrayList<>(list)) {
if (item.equals("B")) {
list.remove(item); // Безопасно
}
}
// Результат: [A, C]
Простой способ, но требует дополнительную память для копии.
Решение 5: Backwards iteration (для List)
// ✓ Итерация в обратном порядке
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i).equals("B")) {
list.remove(i); // Безопасно
}
}
// Результат: [A, C]
Удаление в обратном порядке не влияет на индексы.
Для многопоточности
Синхронизированные коллекции
// ✗ Даже Collections.synchronizedList() требует явной синхронизации
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// Следующий код все еще вызовет исключение:
for (String item : syncList) {
syncList.remove(item); // Ошибка!
}
// ✓ Правильно - синхронизируем блок итерации
syncList = Collections.synchronizedList(new ArrayList<>(Arrays.asList("A", "B", "C")));
synchronized (syncList) {
Iterator<String> it = syncList.iterator();
while (it.hasNext()) {
if (it.next().equals("B")) {
it.remove();
}
}
}
CopyOnWriteArrayList (для многопоточности)
// ✓ Thread-safe коллекция без явной синхронизации
List<String> threadSafeList = new CopyOnWriteArrayList<>(Arrays.asList("A", "B", "C"));
for (String item : threadSafeList) {
if (item.equals("B")) {
threadSafeList.remove(item); // Безопасно
}
}
Сравнение подходов
| Способ | Безопасность | Производительность | Использование |
|---|---|---|---|
| Iterator.remove() | ✓ | ✓✓✓ | Лучший выбор |
| removeIf() | ✓ | ✓✓✓ | Java 8+, лаконично |
| Stream API | ✓ | ✓✓ | Функциональный стиль |
| Копирование | ✓ | ✓ | Простой случай |
| Backwards iteration | ✓ | ✓✓✓ | Только для List |
Выводы
- Никогда не удаляй элементы через list.remove() во время обычной итерации
- Используй Iterator.remove() для явного удаления
- Предпочитай removeIf() и Stream API в современной Java
- Для многопоточности используй CopyOnWriteArrayList
- Помни о синхронизации при работе с synchronized коллекциями