0Pricing
Java Academy · Lekcja

Kontrakty Iterable i Iterator

Poznaj interfejsy Iterable i Iterator oraz dowiedz się, jak wewnętrznie działają pętle for-each.

Kontrakty Iterable i Iterator to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Iterable i Iterator

Iterable<T> i Iterator<T> to interfejsy obsługujące pętlę for-each w języku Java. Ich zrozumienie pozwala tworzyć własne struktury danych zgodne z tą pętlą.

Interfejs Iterable

Iterable<T> ma jedną metodę: iterator(), która zwraca obiekt typu Iterator<T>. Każda klasa implementująca Iterable może być używana w pętli for-each.

// 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
    }
}

Interfejs Iterator

Iterator<T> ma trzy metody: hasNext(), next() oraz opcjonalną 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)
}

Rozwinięcie składni pętli for-each

Pętla for-each jest cukrem składniowym opartym na wzorcu Iterable/Iterator. Zrozumienie tego mechanizmu pomaga poprawnie pisać własne obiekty 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);
}

Stan kursora iteratora

Iterator przechowuje kursor — pozycję w sekwencji. Wywołanie next() przesuwa kursor do przodu. Po wyczerpaniu iteratora nie można go zresetować.

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

Modyfikacja kolekcji podczas iterowania za pomocą jawnego iteratora (poza metodą Iterator.remove()) powoduje zgłoszenie wyjątku 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]

Wiele iteratorów

Każde wywołanie iterator() zwraca nowy, niezależny iterator. Na tej samej kolekcji może jednocześnie działać wiele iteratorów.

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 a Iterator

Najważniejsze rozróżnienie:

  • Iterable: źródło, które może tworzyć iteratory — można po nim iterować wielokrotnie
  • Iterator: kursor poruszający się po sekwencji — jednorazowy i przechowujący stan
// 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!

Praktyka: własny obiekt Iterable dla wierszy pliku

Czytnik pliku implementujący Iterable, który umożliwia użycie pętli for-each do iterowania po jego wierszach.

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(); }
}

Domyślna metoda forEach

W Java 8 do interfejsu Iterable dodano domyślną metodę forEach(Consumer). Jest to wygodna alternatywa dla pisania jawnych pętli.

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);

Wzorzec iteratora w praktyce

Podsumowanie sytuacji, w których warto implementować Iterable/Iterator:

  • Niestandardowe struktury danych (drzewa, grafy, listy wiązane)
  • Leniwe sekwencje generujące elementy na żądanie
  • Sekwencje oparte na zasobach (wiersze pliku, kursor bazy danych, kolejka komunikatów)

Szybkie sprawdzenie

Jaki wyjątek jest zgłaszany podczas modyfikowania kolekcji w trakcie iterowania po niej za pomocą pętli for-each?

Podsumowanie: kontrakty Iterable i Iterator

Najważniejsze informacje:

  • Iterable ma jedną metodę: iterator() — umożliwia obsługę pętli for-each
  • Iterator ma metody hasNext(), next() oraz opcjonalną remove()
  • Pętla for-each jest cukrem składniowym opartym na wzorcu Iterable/Iterator
  • Wywołuj iterator() wielokrotnie, aby uzyskać niezależne, nowe kursory
  • Nigdy nie modyfikuj kolekcji podczas iterowania za pomocą pętli for-each — zamiast tego używaj Iterator.remove()
  • Implementuj Iterable dla własnych struktur danych, aby obsługiwały pętle for-each

Często zadawane pytania

Czy lekcja „Kontrakty Iterable i Iterator” jest bezpłatna?

Tak — pełny tekst „Kontrakty Iterable i Iterator” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Kontrakty Iterable i Iterator”?

Poznaj interfejsy Iterable i Iterator oraz dowiedz się, jak wewnętrznie działają pętle for-each. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Kontrakty Iterable i Iterator”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Kontrakty Iterable i Iterator
  2. Implementowanie własnego iteratora
  3. ListIterator i przechodzenie dwukierunkowe
  4. Iteratory fail-fast a fail-safe
← Powrót do Java Academy