0Pricing
Java Academy · Урок

Контракты 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()); // 2

Iterable и 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 — локальная установка не требуется.

Все уроки этого курса

  1. Контракты Iterable и Iterator
  2. Реализация собственного итератора
  3. ListIterator и двунаправленный обход
  4. Отказоустойчивые и безопасные итераторы
← Назад к Java Academy