← Назад к вопросам

Как обойти модификацию Hash таблицы

2.0 Middle🔥 151 комментариев
#ООП#Основы Java

Комментарии (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

Выводы

  1. Никогда не удаляй элементы через list.remove() во время обычной итерации
  2. Используй Iterator.remove() для явного удаления
  3. Предпочитай removeIf() и Stream API в современной Java
  4. Для многопоточности используй CopyOnWriteArrayList
  5. Помни о синхронизации при работе с synchronized коллекциями