Iterable
Iterable<T> — источник итератора. Его реализация позволяет использовать
объект справа от for-each.
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) { /* ... */ }
default Spliterator<T> spliterator() { /* ... */ }
}Iterable, Iterator и for-each
Iterable создаёт обход, а Iterator хранит состояние одного обхода.
Каждый вызов iterator() обычно возвращает новый независимый объект.
for (String item : names) {
use(item);
}Для Iterable компилятор превращает это примерно в:
for (Iterator<String> it = names.iterator(); it.hasNext(); ) {
String item = it.next();
use(item);
}Массивы также поддерживают for-each, хотя не реализуют Iterable.
Контракт Iterator
hasNext()сообщает, есть ли следующий элемент, и не сдвигает позицию;next()возвращает элемент и сдвигает позицию;next()после конца бросаетNoSuchElementException;remove()— необязательная операция и удаляет последний выданный элемент.
final class Range implements Iterable<Integer> {
private final int from;
private final int to;
Range(int from, int to) {
this.from = from;
this.to = to;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
private int current = from;
public boolean hasNext() {
return current < to;
}
public Integer next() {
if (!hasNext()) throw new NoSuchElementException();
return current++;
}
};
}
}Изменение во время обхода
У многих коллекций iterator fail-fast: внешнее структурное изменение может
привести к ConcurrentModificationException. Корректное удаление в
однопоточном обходе выполняют через Iterator.remove() или removeIf.
Это best-effort проверка, не механизм синхронизации.
Spliterator дополнительно умеет делить источник на части и описывать его
свойства. На нём строится создание последовательных и параллельных
stream.
Вопросы для собеседования
- Чем Iterable отличается от Iterator? Первый создаёт обход, второй хранит позицию конкретного обхода.
- Можно ли повторно использовать Iterator? После исчерпания — обычно нет;
получают новый через
iterator(). - Что обязан сделать
nextпосле конца? БроситьNoSuchElementException. - Почему массив работает в for-each? Для массивов это отдельное правило языка.
- Как удалять при обходе? Через iterator или специальные методы коллекции.
- Гарантирован ли
ConcurrentModificationException? Нет, это best effort. - Зачем Spliterator? Для разбиения обхода и передачи характеристик stream.