Контракты Iterable и Iterator
Поймите интерфейсы Iterable и Iterator и узнайте, как внутри работают циклы for-each.
«Контракты Iterable и Iterator» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Iterable и Iterator
Iterable<T> и Iterator<T> — это интерфейсы, лежащие в основе цикла перебора в Java. Понимание этих интерфейсов позволяет создавать собственные структуры данных, совместимые с таким циклом.
Интерфейс Iterable
Iterable<T> содержит один метод: iterator(), который возвращает Iterator<T>. Любой класс, реализующий Iterable, можно использовать в цикле перебора.
// java.lang.Iterable<T>
interface Iterable<T> {
Iterator<T> iterator();
// default: forEach, spliterator (Java 8+)
}
// Any class implementing Iterable<T> works in for-each:
class Range implements Iterable<Integer> {
private final int start, end;
Range(int start, int end) { this.start = start; this.end = end; }
public Iterator<Integer> iterator() {
return new RangeIterator(); // defined separately
}
}Интерфейс Iterator
Iterator<T> содержит три метода: hasNext(), next() и необязательный remove().
// java.util.Iterator<T>
interface Iterator<T> {
boolean hasNext(); // true if more elements
T next(); // returns next element, advances cursor
default void remove() { throw new UnsupportedOperationException(); }
}
// How for-each desugars internally:
// for (T item : iterable) { use(item); }
// becomes:
Iterator<T> it = iterable.iterator();
while (it.hasNext()) {
T item = it.next();
// use(item)
}Разворачивание цикла перебора
Цикл перебора представляет собой синтаксический сахар над шаблоном Iterable/Iterator. Понимание этого помогает правильно писать собственные реализации Iterable.
List<String> names = List.of("Alice", "Bob", "Charlie");
// For-each (readable way)
for (String name : names) {
System.out.println(name);
}
// Equivalent explicit form
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}Состояние курсора Iterator
Iterator хранит курсор — позицию в последовательности. Вызов next() перемещает курсор вперёд. После исчерпания Iterator нельзя начать заново.
List<Integer> nums = List.of(1, 2, 3);
Iterator<Integer> it = nums.iterator();
System.out.println(it.hasNext()); // true
System.out.println(it.next()); // 1
System.out.println(it.next()); // 2
System.out.println(it.next()); // 3
System.out.println(it.hasNext()); // false
try {
it.next(); // NoSuchElementException
} catch (java.util.NoSuchElementException e) {
System.out.println("No more elements!");
}ConcurrentModificationException
Изменение коллекции во время перебора с помощью явного Iterator (за пределами Iterator.remove()) приводит к ConcurrentModificationException.
List<String> list = new ArrayList<>(List.of("a", "b", "c", "d"));
// BAD: modifying collection during for-each
try {
for (String s : list) {
if ("b".equals(s)) list.remove(s); // ConcurrentModificationException!
}
} catch (java.util.ConcurrentModificationException e) {
System.out.println("Cannot modify during iteration!");
}
// GOOD: use Iterator.remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if ("b".equals(it.next())) it.remove(); // safe
}
System.out.println(list); // [a, c, d]Несколько Iterator
Каждый вызов iterator() возвращает новый независимый Iterator. В одной коллекции одновременно могут работать несколько Iterator.
List<Integer> nums = List.of(1, 2, 3);
Iterator<Integer> a = nums.iterator();
Iterator<Integer> b = nums.iterator();
System.out.println(a.next()); // 1
System.out.println(b.next()); // 1 (independent cursor)
System.out.println(a.next()); // 2
System.out.println(b.next()); // 2Iterable и Iterator
Ключевое различие:
- Iterable: источник, способный создавать Iterator; его можно перебирать несколько раз
- Iterator: курсор над последовательностью; одноразовый и хранящий состояние
// Iterable: reusable
List<String> list = List.of("x", "y");
for (String s : list) {} // OK
for (String s : list) {} // OK again — new iterator each time
// Iterator: single-use
Iterator<String> it = list.iterator();
while (it.hasNext()) it.next();
// for (String s : it) {} // compile error — Iterator is not Iterable!Практика: собственный Iterable для строк файла
Средство чтения файла, реализующее Iterable и позволяющее перебирать его строки с помощью цикла перебора.
import java.io.*;
import java.util.*;
class FileLines implements Iterable<String>, Closeable {
private final BufferedReader reader;
FileLines(String path) throws IOException {
this.reader = new BufferedReader(new FileReader(path));
}
public Iterator<String> iterator() {
return new Iterator<>() {
private String nextLine = readNext();
private String readNext() {
try { return reader.readLine(); }
catch (IOException e) { return null; }
}
public boolean hasNext() { return nextLine != null; }
public String next() {
String curr = nextLine;
nextLine = readNext();
return curr;
}
};
}
public void close() throws IOException { reader.close(); }
}Метод forEach по умолчанию
В Java 8 в Iterable был добавлен метод forEach(Consumer) по умолчанию. Это удобная альтернатива написанию явных циклов.
List<String> cities = List.of("New York", "London", "Tokyo");
// for-each loop
for (String city : cities) System.out.println(city);
// forEach with lambda
cities.forEach(city -> System.out.println(city));
// forEach with method reference (most concise)
cities.forEach(System.out::println);
// For transforming: use stream
cities.stream()
.map(String::toUpperCase)
.forEach(System.out::println);Шаблон Iterator на практике
Когда следует реализовывать Iterable/Iterator:
- собственные структуры данных (деревья, графы, связные списки)
- ленивые последовательности, создающие элементы по требованию
- последовательности на основе ресурсов (строки файла, курсор базы данных, очередь сообщений)
Быстрая проверка
Какое исключение возникает при изменении коллекции во время перебора с помощью цикла перебора?
Повторение: контракты Iterable и Iterator
Основные выводы:
- Iterable<T> содержит один метод: iterator() — обеспечивает поддержку цикла перебора
- Iterator<T> содержит hasNext(), next() и необязательный remove()
- Цикл перебора представляет собой синтаксический сахар над шаблоном Iterable/Iterator
- Вызывайте iterator() несколько раз, чтобы получить независимые новые курсоры
- Никогда не изменяйте коллекцию во время перебора — вместо этого используйте Iterator.remove()
- Реализуйте Iterable для собственных структур данных, чтобы поддержать циклы перебора
Часто задаваемые вопросы
Урок «Контракты Iterable и Iterator» бесплатный?
Да — полный текст урока «Контракты Iterable и Iterator» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Контракты Iterable и Iterator»?
Поймите интерфейсы Iterable и Iterator и узнайте, как внутри работают циклы for-each. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Контракты Iterable и Iterator»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Контракты Iterable и Iterator
- Реализация собственного итератора
- ListIterator и двунаправленный обход
- Отказоустойчивые и безопасные итераторы