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().
• почему `for-each` нельзя использовать для удаления, а `Iterator.remove()` можно (если дошёл до 2-го слоя).