Какое исключение выбрасывается при Fail-Fast поведении в коллекциях Java?

«Какое исключение выбрасывается при Fail-Fast поведении в коллекциях Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

При Fail-Fast поведении выбрасывается ConcurrentModificationException.

Почему это происходит? Итераторы большинства стандартных коллекций (например, ArrayList, HashMap) являются fail-fast. Они отслеживают количество структурных модификаций коллекции через внутренний счетчик modCount. Если во время итерации коллекция была изменена не через методы самого итератора (например, напрямую добавлен или удален элемент), счетчики перестают совпадать, и итератор выбрасывает исключение.

Пример, вызывающий исключение:

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String item : list) {
    if (item.equals("B")) {
        list.remove(item); // ConcurrentModificationException!
    }
}

Как избежать?

  1. Использовать методы самого итератора для удаления:
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        String item = iterator.next();
        if (item.equals("B")) {
            iterator.remove(); // Безопасное удаление
        }
    }
  2. Использовать потокобезопасные коллекции, предназначенные для конкурентного изменения (например, CopyOnWriteArrayList).