Java Academy · Oppitunti

Iterable- ja Iterator-sopimukset

Ymmärrä Iterable- ja Iterator-rajapinnat sekä se, miten for-each-silmukat toimivat sisäisesti.

Oppitunti 1/413 vaihetta

Iterable- ja Iterator-sopimukset on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 1/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.

Iterable ja Iterator

Iterable<T> ja Iterator<T> ovat rajapinnat, joiden varaan Javan for-each-silmukka rakentuu. Niiden ymmärtäminen auttaa tekemään omista tietorakenteistanne silmukkayhteensopivia.

Iterable-rajapinta

Iterable<T>-rajapinnassa on yksi metodi: iterator(), joka palauttaa Iterator<T>-olion. Mitä tahansa Iterable-rajapinnan toteuttavaa luokkaa voi käyttää for-each-silmukassa.

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

Iterator<T>-rajapinnassa on kolme metodia: hasNext(), next() ja valinnainen 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)
}

For-each-silmukan purkaminen

For-each-silmukka on Iterable/Iterator-mallin syntaktista sokeria. Tämän ymmärtäminen auttaa kirjoittamaan mukautetut Iterable-toteutukset oikein.

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

Iteratorin kursorin tila

Iterator ylläpitää kursoria eli sijaintia sekvenssissä. next()-kutsun tekeminen siirtää kursoria eteenpäin. Kun iterator on käyty loppuun, sitä ei voi palauttaa alkutilaan.

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

Kokoelman muokkaaminen iteroinnin aikana eksplisiittistä iteratoria käyttäen (muutoin kuin Iterator.remove()-kutsulla) aiheuttaa poikkeuksen 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]

Useita iteraattoreita

Jokainen iterator()-kutsu palauttaa uuden, itsenäisen iteraattorin. Samassa kokoelmassa voi olla samanaikaisesti aktiivisia useita iteraattoreita.

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

Keskeinen ero:

  • Iterable: lähde, joka voi tuottaa iteraattoreita — voidaan iteroida useita kertoja
  • Iterator: sekvenssin kursori — kertakäyttöinen ja tilallinen
// 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!

Käytännön harjoitus: mukautettu tiedoston rivien Iterable

Tiedostonlukija, joka toteuttaa Iterable-rajapinnan ja mahdollistaa tiedoston rivien läpikäynnin for-each-silmukalla.

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-oletusmetodi

Java 8 lisäsi Iterable-rajapintaan oletusmetodin forEach(Consumer). Se on kätevä vaihtoehto eksplisiittisten silmukoiden kirjoittamiselle.

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-malli käytännössä

Yhteenveto tilanteista, joissa Iterable/Iterator kannattaa toteuttaa:

  • Mukautetut tietorakenteet, kuten puu, graafi tai linkitetty lista
  • Laiskat sekvenssit, jotka tuottavat alkioita tarpeen mukaan
  • Resurssipohjaiset sekvenssit, kuten tiedoston rivit, tietokantakursori tai viestijono

Pikatarkistus

Mikä poikkeus aiheutuu, kun kokoelmaa muokataan for-each-silmukalla iteroinnin aikana?

Kertaus: Iterable- ja Iterator-sopimukset

Tärkeimmät opit:

  • Iterable:llä on yksi metodi: iterator() — mahdollistaa for-each-silmukan käytön
  • Iterator:llä on metodit hasNext(), next() ja valinnainen remove()
  • For-each-silmukka on Iterable/Iterator-mallin syntaktista sokeria
  • Kutsu iterator()-metodia useita kertoja saadaksesi toisistaan riippumattomat uudet kursorit
  • Älä koskaan muokkaa kokoelmaa for-each-silmukan aikana — käytä sen sijaan Iterator.remove()-metodia
  • Toteuta mukautetuille tietorakenteille Iterable, jotta ne tukevat for-each-silmukoita
Aloita maksutta

Opi Java tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
104
Oppitunnit
374

Usein kysytyt kysymykset

Onko oppitunti ”Iterable- ja Iterator-sopimukset” ilmainen?

Kyllä – oppitunnin ”Iterable- ja Iterator-sopimukset” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Iterable- ja Iterator-sopimukset”?

Ymmärrä Iterable- ja Iterator-rajapinnat sekä se, miten for-each-silmukat toimivat sisäisesti. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 1/4.

Kuinka kauan ”Iterable- ja Iterator-sopimukset”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?

Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Iterable- ja Iterator-sopimukset
  2. Mukautetun iteraattorin toteuttaminen
  3. ListIterator ja kaksisuuntainen läpikäynti
  4. Fail-fast- ja fail-safe-iteraattorit
← Takaisin: Java Academy