0Pricing
Java Academy · レッスン

カスタム Iterator の実装

単純な連結リストや範囲構造のためのカスタム Iterator クラスを作成します。

「カスタム Iterator の実装」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

カスタムIterator

カスタムiteratorを構築すると、データ構造の走査方法を完全に制御できます。このレッスンでは、連結リストのiteratorを段階的に実装します。

Nodeクラス

まず、単方向連結リストのノード構造を定義します。

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

Iteratorの実装

現在のノードを指すカーソルを持つIterator<T>を実装する内部クラスを作成します。

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と完全なLinkedList

ノードを先頭に追加する機能を実装し、完全に動作するクラスを確認します。

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

カスタムIteratorの使用

Iterableインターフェースを実装すると、連結リストをfor-eachループや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));

範囲Iterator

より簡単な例として、基盤となるデータ構造を持たず、数値範囲を反復処理するiteratorを見てみましょう。

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

明示的なスタックを使ってBSTの中順走査iteratorを実装します。iteratorによって再帰的な走査を置き換えられることを示します。

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

遅延Iterator

Iteratorは、next()が呼び出されたときだけ値を生成する遅延処理ができます。無限シーケンスに便利です。

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

フィルターIterator

別のiteratorをラップし、述語に一致しない要素をスキップするデコレーターiteratorです。

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

IteratorとStreamの連携

カスタムiteratorは、Spliterators.spliteratorUnknownSize()を使ってStreamに適応できます。

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

反復処理中の削除

Iteratorの任意実装メソッドremove()は、直前のnext()呼び出しで返された要素を削除します。カスタムiteratorでは明示的に実装する必要があります。

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

Iteratorのチェックリスト

カスタムIteratorを実装するとき:

  • next()の前に必ずhasNext()を呼び出します
  • 空の場合、next()からnullを返すのではなく、NoSuchElementExceptionをスローします
  • コレクションに依存する状態をiteratorに持たせません(コレクションのサイズをキャッシュしないでください)
  • 必要に応じてmodCountを使い、同時変更を検出します

クイックチェック

要素がもうない場合、next()は何をスローすべきですか。

まとめ:カスタムIteratorの実装

重要なポイント:

  • hasNext()、next()、そして任意実装のremove()を備えたIteratorを実装します
  • iterator内に、次の要素を指すカーソルフィールドを保持します
  • hasNext()がfalseの場合、next()からNoSuchElementExceptionをスローします
  • 独立したカーソルを得るため、iterator()が呼び出されるたびに新しいiteratorインスタンスを作成します
  • 遅延iteratorは必要に応じて値を生成するため、無限シーケンスに便利です
  • IteratorをStreamSupport.stream()でラップし、Stream APIに接続します

よくある質問

「カスタム Iterator の実装」レッスンは無料ですか?

はい。「カスタム Iterator の実装」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「カスタム Iterator の実装」で何を学びますか?

単純な連結リストや範囲構造のためのカスタム Iterator クラスを作成します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「カスタム Iterator の実装」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Iterable と Iterator の契約
  2. カスタム Iterator の実装
  3. ListIterator と双方向走査
  4. Fail-Fast と Fail-Safe の Iterator
← Java Academyに戻る