Iterator

Iterator — универсальный способ пройтись по любой коллекции по одному элементу. Именно он работает под капотом у for-each:

List<String> names = List.of("Артур", "Борис");

Iterator<String> it = names.iterator();
while (it.hasNext()) {
    String name = it.next();
    System.out.println(name);
}

// то же самое, но короче — компилятор сам превращает это в Iterator
for (String name : names) {
    System.out.println(name);
}

hasNext() — «есть ли ещё элемент», next() — «дай следующий и сдвинься».

Копнуть глубже

Главная причина писать Iterator руками — безопасное удаление во время обхода. Обычный for-each нельзя использовать, если внутри цикла удаляешь элементы — будет ConcurrentModificationException:

for (String name : names) {
    if (name.equals("Артур")) {
        names.remove(name);   // 💥 ConcurrentModificationException
    }
}

Iterator же умеет удалять безопасно — у него есть свой метод remove():

Iterator<String> it = names.iterator();
while (it.hasNext()) {
    if (it.next().equals("Артур")) {
        it.remove();   // ✅ безопасно — итератор сам знает про изменение
    }
}
Под капотом

Iterator следит за modCount — счётчиком изменений коллекции. При создании итератор запоминает текущее значение modCount. Если коллекцию поменяли в обход итератора (например, вызвали list.remove() напрямую внутри for-each), modCount изменится, и на следующем next() итератор это заметит и бросит ConcurrentModificationException — это называется fail-fast поведение, оно специально защищает от непредсказуемых багов при параллельном изменении.

Метод it.remove() обновляет modCount синхронно с самим итератором — поэтому он не триггерит исключение, в отличие от прямого вызова list.remove().

🎤 Закрыл тему, если можешь объяснить:
• что делают `hasNext()`/`next()` и как с этим связан `for-each`;
• почему `for-each` нельзя использовать для удаления, а `Iterator.remove()` можно (если дошёл до 2-го слоя).