Что такое Iterator pattern?
4. Fail-Safe коллекции для многопоточности 5. Не модифицируйте коллекцию во время итерации
🟢 Junior Level
Iterator — паттерн для перебора элементов коллекции, не зная её внутреннего устройства.
Простая аналогия: Пульт от телевизора. Вы переключаете каналы кнопкой “Next”, не зная, как телевизор внутри переключает каналы.
Пример:
// Iterator в Java — это то, что использует forEach
List<String> list = List.of("A", "B", "C");
// Явный Iterator
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
// То же самое через forEach (использует Iterator внутри)
list.forEach(System.out::println);
Зачем нужен:
- Единый способ перебора для любых коллекций
- Не нужно знать внутреннее устройство
- Можно удалять элементы во время перебора
🟡 Middle Level
Iterator vs ListIterator
List<String> list = new ArrayList<>(List.of("A", "B", "C"));
// List.of() возвращает immutable список, add() выбросит UnsupportedOperationException
// Iterator — только вперёд
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
it.remove(); // Можно удалять
}
// ListIterator — вперёд и назад
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
lit.next();
lit.add("new"); // Можно добавлять!
}
while (lit.hasPrevious()) {
lit.previous(); // Можно назад!
}
Собственный Iterator
class Tree<T> {
private Node<T> root;
public Iterator<T> iterator() {
return new TreeIterator();
}
private class TreeIterator implements Iterator<T> {
private Queue<Node<T>> queue = new LinkedList<>();
TreeIterator() {
if (root != null) queue.add(root);
}
public boolean hasNext() {
return !queue.isEmpty();
}
public T next() {
Node<T> node = queue.poll();
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
return node.value;
}
}
}
🔴 Senior Level
Fail-Fast vs Fail-Safe
// Fail-Fast (ArrayList, HashMap)
List<String> list = new ArrayList<>(List.of("A", "B", "C"));
Iterator<String> it = list.iterator();
it.next();
list.add("D"); // Модификация коллекции
it.next(); // → ConcurrentModificationException!
// Fail-Safe (CopyOnWriteArrayList, ConcurrentHashMap)
List<String> safeList = new CopyOnWriteArrayList<>(List.of("A", "B", "C"));
Iterator<String> safeIt = safeList.iterator();
safeIt.next();
safeList.add("D"); // Модификация
safeIt.next(); // Не бросает exception, но итератор видит только элементы на момент создания (snapshot). Новый элемент "D" НЕ будет виден в текущей итерации.
Stream API как Iterator
// Stream API использует Spliterator internally, но предоставляет более высокоуровневый API
// (lazy evaluation, parallel streams, functional composition).
list.stream()
.filter(s -> s.startsWith("A"))
.map(String::toUpperCase)
.forEach(System.out::println);
// Stream поддерживает lazy evaluation
// Iterator — нет
Production Experience
Реальный сценарий: Fail-Faster баг
- Удаление элемента в цикле forEach → ConcurrentModificationException
- Решение: iterator.remove() или removeIf()
- Урок: нельзя модифицировать коллекцию во время итерации
Best Practices
- iterator.remove() для удаления во время перебора
- removeIf() для фильтрации (Java 8+)
- Stream для сложных операций
- Fail-Safe коллекции для многопоточности
- Не модифицируйте коллекцию во время итерации
Резюме для Senior
- Iterator = универсальный способ перебора
- Fail-Fast = exception при модификации
- Fail-Safe = итератор по копии
- Stream API = использует Spliterator, предоставляет lazy evaluation и functional composition
- removeIf() предпочтительнее ручного удаления
🎯 Шпаргалка для интервью
Обязательно знать:
- Iterator — универсальный способ перебора, скрывающий внутреннее устройство коллекции
- Fail-Fast (ArrayList, HashMap) — ConcurrentModificationException при модификации во время итерации
- Fail-Safe (CopyOnWriteArrayList, ConcurrentHashMap) — итератор по snapshot, не бросает exception
- Stream API использует Spliterator — lazy evaluation, parallel streams, functional composition
- Для удаления во время итерации: iterator.remove() или removeIf() (Java 8+)
- ListIterator расширяет Iterator: движение назад, добавление элементов
- Нельзя модифицировать коллекцию во время итерации (кроме iterator.remove() и Fail-Safe коллекций)
Частые уточняющие вопросы:
- Как удалить элемент во время перебора? — iterator.remove() или list.removeIf(predicate)
- Чем Fail-Fast отличается от Fail-Safe? — Fail-Fast бросает exception, Fail-Safe работает по snapshot
- Почему forEach не даёт удалять элементы? — forEach использует Iterator внутри, модификация → ConcurrentModificationException
- Что такое Spliterator? — Parallel-capable Iterator для Stream API, поддерживает partitioning
Красные флаги (НЕ говорить):
- “Я модифицирую коллекцию в цикле forEach” — ConcurrentModificationException гарантирован
- “Fail-Safe означает что данные всегда актуальны” — итератор видит snapshot, новые элементы НЕ видны
- “Iterator устарел с приходом Stream API” — Iterator используется под капотом Stream и forEach
- “removeIf() — это то же самое что iterator.remove()” — removeIf() принимает Predicate, более высокоуровневый
Связанные темы:
- [[11. Как реализован Observer в Java]] — перебор слушателей
- [[2. Какие категории паттернов существуют]] — Behavioral паттерны
- [[10. Когда использовать Strategy]] — Stream API как альтернатива
- [[16. Какие антипаттерны вы знаете]] — God Object с коллекциями
- [[1. Что такое паттерны проектирования]] — Iterator в Modern Java