Java Academy · Lektion

Implementera en egen iterator

Bygg en egen iteratorklass för en enkel länkad lista eller intervallstruktur.

Lektion 2 av 414 steg

Implementera en egen iterator är en gratis lektion i Java Academy på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Java Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Java Academy innehåller totalt 4 lektioner.

Anpassad iterator

Genom att bygga en anpassad iterator får du full kontroll över hur en datastruktur genomgås. I den här lektionen går vi steg för steg igenom hur en iterator för en länkad lista implementeras.

Nodklassen

Definiera först nodstrukturen för en enkellänkad lista.

class Node<T> {
    final T value;
    Node<T> next;

    Node(T value) {
        this.value = value;
    }
}

// Building a chain: 1 -> 2 -> 3
Node<Integer> head = new Node<>(1);
head.next = new Node<>(2);
head.next.next = new Node<>(3);

Implementera iteratorn

Skapa en inre klass som implementerar Iterator<T> och har en cursor som pekar på den aktuella noden.

import java.util.Iterator;
import java.util.NoSuchElementException;

class LinkedList<T> implements Iterable<T> {
    private Node<T> head;
    private int size;

    private class LinkedListIterator implements Iterator<T> {
        private Node<T> current = head; // cursor

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public T next() {
            if (!hasNext()) throw new NoSuchElementException();
            T value = current.value;
            current = current.next;
            return value;
        }
    }

    @Override
    public Iterator<T> iterator() {
        return new LinkedListIterator();
    }
}

addFirst och komplett LinkedList

Lägg till möjligheten att lägga till noder först och se den fullständiga fungerande klassen.

class LinkedList<T> implements Iterable<T> {
    private Node<T> head;
    private int size;

    public void addFirst(T value) {
        Node<T> node = new Node<>(value);
        node.next = head;
        head = node;
        size++;
    }

    public void addLast(T value) {
        Node<T> node = new Node<>(value);
        if (head == null) { head = node; }
        else {
            Node<T> curr = head;
            while (curr.next != null) curr = curr.next;
            curr.next = node;
        }
        size++;
    }

    public int size() { return size; }

    @Override
    public Iterator<T> iterator() {
        return new LinkedListIterator();
    }
}

Använda den anpassade iteratorn

När Iterable-gränssnittet är implementerat fungerar den länkade listan i for-each-loopar och med forEach.

LinkedList<String> list = new LinkedList<>();
list.addLast("Alice");
list.addLast("Bob");
list.addLast("Charlie");

// For-each loop works!
for (String name : list) {
    System.out.println(name);
}
// Alice
// Bob
// Charlie

// Stream also works (Java 8+)
list.forEach(name -> System.out.println("Hello, " + name));

Intervalliterator

Ett enklare exempel: en iterator över ett numeriskt intervall utan en underliggande datastruktur.

class IntRange implements Iterable<Integer> {
    private final int start, end, step;

    IntRange(int start, int end, int step) {
        this.start = start; this.end = end; this.step = step;
    }
    IntRange(int start, int end) { this(start, end, 1); }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<>() {
            int current = start;
            public boolean hasNext() { return current < end; }
            public Integer next() {
                if (!hasNext()) throw new NoSuchElementException();
                int val = current;
                current += step;
                return val;
            }
        };
    }
}

for (int n : new IntRange(0, 10, 2)) System.out.print(n + " ");
// 0 2 4 6 8

Iterator för trädets inorder-genomgång

Implementering av en inorder-iterator för ett BST med hjälp av en explicit stack – visar hur iteratorer kan ersätta rekursiv genomgång.

import java.util.*;

class BinaryTree<T extends Comparable<T>> {
    private record TreeNode<T>(T val, TreeNode<T> left, TreeNode<T> right) {}

    private TreeNode<T> root;

    public Iterator<T> inorderIterator() {
        Deque<TreeNode<T>> stack = new ArrayDeque<>();
        pushLeft(root, stack);
        return new Iterator<>() {
            public boolean hasNext() { return !stack.isEmpty(); }
            public T next() {
                TreeNode<T> node = stack.pop();
                pushLeft(node.right(), stack);
                return node.val();
            }
        };
    }

    private void pushLeft(TreeNode<T> node, Deque<TreeNode<T>> stack) {
        while (node != null) { stack.push(node); node = node.left(); }
    }
}

Fördröjd iterator

Iteratorer kan generera värden fördröjt – endast när next() anropas. Det är användbart för oändliga sekvenser.

class FibonacciIterator implements Iterator<Long> {
    private long a = 0, b = 1;

    @Override public boolean hasNext() { return true; } // infinite!

    @Override public Long next() {
        long result = a;
        long next = a + b;
        a = b;
        b = next;
        return result;
    }
}

Iterator<Long> fib = new FibonacciIterator();
for (int i = 0; i < 10; i++) System.out.print(fib.next() + " ");
// 0 1 1 2 3 5 8 13 21 34

Filtrerande iterator

En dekoratoriterator som omsluter en annan iterator och hoppar över element som inte matchar ett predikat.

import java.util.*;
import java.util.function.*;

class FilterIterator<T> implements Iterator<T> {
    private final Iterator<T> source;
    private final Predicate<T> predicate;
    private T next;
    private boolean hasNext;

    FilterIterator(Iterator<T> source, Predicate<T> predicate) {
        this.source = source; this.predicate = predicate;
        advance();
    }

    private void advance() {
        hasNext = false;
        while (source.hasNext()) {
            T candidate = source.next();
            if (predicate.test(candidate)) { next = candidate; hasNext = true; break; }
        }
    }

    public boolean hasNext() { return hasNext; }
    public T next() { T val = next; advance(); return val; }
}

List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,10);
Iterator<Integer> evens = new FilterIterator<>(nums.iterator(), n -> n % 2 == 0);
while (evens.hasNext()) System.out.print(evens.next() + " ");
// 2 4 6 8 10

Integration mellan iteratorer och strömmar

Anpassade iteratorer kan anpassas till Streams med hjälp av Spliterators.spliteratorUnknownSize().

import java.util.*;
import java.util.stream.*;

Iterator<Integer> rangeIt = new IntRange(1, 6).iterator();

Stream<Integer> stream = StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(rangeIt, Spliterator.ORDERED),
    false // not parallel
);

int sum = stream.mapToInt(Integer::intValue).sum();
System.out.println(sum); // 15

Ta bort under iteration

Den valfria metoden remove() i Iterator tar bort elementet som returnerades av det senaste anropet till next() – den måste implementeras uttryckligen i anpassade iteratorer.

class MutableLinkedList<T> implements Iterable<T> {
    // ... (full implementation)

    // Iterator with remove support
    private class RemovableIterator implements Iterator<T> {
        private Node<T> prev = null;
        private Node<T> current = head;

        public boolean hasNext() { return current != null; }
        public T next() {
            prev = (prev == null) ? null : current;
            T val = current.value;
            current = current.next;
            return val;
        }

        public void remove() {
            // Remove the last returned node
            if (prev == null) head = current;
            else prev.next = current;
            size--;
        }
    }
}

Checklista för iteratorer

Vid implementering av en anpassad Iterator:

  • Anropa alltid hasNext() före next()
  • Kasta NoSuchElementException (returnera inte null) från next() när iteratorn är tom
  • Gör iteratorn tillståndslös i förhållande till samlingen (cacha inte samlingens storlek)
  • Använd modCount för att upptäcka samtidiga ändringar vid behov

Snabb kontroll

Vad ska next() kasta när det inte finns fler element?

Sammanfattning: Implementera en anpassad iterator

Viktiga lärdomar:

  • Implementera Iterator med hasNext(), next() och den valfria remove()
  • Underhåll ett cursor-fält i iteratorn som pekar på nästa element
  • Kasta NoSuchElementException från next() när hasNext() är false
  • Skapa en ny iteratorinstans för varje anrop till iterator() för att få oberoende cursorer
  • Fördröjda iteratorer genererar värden vid behov – användbart för oändliga sekvenser
  • Omslut iteratorer i StreamSupport.stream() för att ansluta dem till Stream API
Gratis att börja

Lär dig Java med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
104
Lektioner
374

Vanliga frågor

Är lektionen ”Implementera en egen iterator” gratis?

Ja – hela texten till ”Implementera en egen iterator” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Java Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i Java Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Implementera en egen iterator”?

Bygg en egen iteratorklass för en enkel länkad lista eller intervallstruktur. Ni övar på Java Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Java Academy?

Du behöver inga förkunskaper. Utbildningen i Java Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Implementera en egen iterator”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Java Academy-lektionen?

Ja. Varje Java Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Kontrakten för Iterable och Iterator
  2. Implementera en egen iterator
  3. ListIterator och dubbelriktad traversering
  4. Fail-fast kontra fail-safe-iteratorer
← Tillbaka till Java Academy