【プログラミングしてみよう45】JavaのIteratorとIterableの仕組みを理解する

Java

前回はReflectionを使ってクラス情報を動的に操作する方法を学びました。

今回は「Iterator(イテレータ)」と「Iterable(イテラブル)」について解説します。 これまで拡張for文でリストや配列を繰り返し処理してきましたが、その裏側でどのような仕組みが動いているのかを理解することで、自分でも繰り返し処理に対応したクラスを作れるようになります。

IteratorとIterableとは

拡張for文(for-each文)は、リストや配列の要素を順番に取り出す便利な書き方です。

List<String> names = List.of("ぱんまる", "たろう", "はなこ");
for (String name : names) {
    System.out.println(name);
}

この裏側では Iterator という仕組みが使われています。Iterator は「次の要素があるか」「次の要素を取り出す」という2つの操作を提供するインターフェースです。

public interface Iterator<E> {
    boolean hasNext(); // 次の要素があれば true
    E next();          // 次の要素を返す
}

Iterable は「Iteratorを返せる」ことを示すインターフェースで、これを実装したクラスは拡張for文で使えるようになります。

public interface Iterable<T> {
    Iterator<T> iterator(); // Iteratorを返す
}

ArrayListやHashSetなどのコレクションクラスはすべて Iterable を実装しているため、拡張for文で使えます。

サンプルコード(Iteratorの基本)

ファイル作成

「Java33」フォルダに IteratorTest.java という名前でファイルを作成します

コード入力

以下のコードを入力します。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorTest {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("ぱんまる");
        names.add("たろう");
        names.add("はなこ");

        // 拡張for文(内部でIteratorを使っている)
        System.out.println("--- 拡張for文 ---");
        for (String name : names) {
            System.out.println(name);
        }

        // Iteratorを直接使う書き方(上と同じ動作)
        System.out.println("\n--- Iteratorを直接使う ---");
        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            System.out.println(name);
        }

        // Iteratorを使ってループ中に要素を削除する
        List<String> scores = new ArrayList<>();
        scores.add("apple");
        scores.add("banana");
        scores.add("cherry");
        scores.add("apricot");

        System.out.println("\n削除前: " + scores);

        Iterator<String> it = scores.iterator();
        while (it.hasNext()) {
            String fruit = it.next();
            if (fruit.startsWith("a")) {
                it.remove(); // ループ中の安全な削除
            }
        }

        System.out.println("「a」始まりを削除後: " + scores);
    }
}

コンパイル

javac IteratorTest.java

実行

java IteratorTest

実行結果は次のようになります。

--- 拡張for文 ---
ぱんまる
たろう
はなこ

--- Iteratorを直接使う ---
ぱんまる
たろう
はなこ

削除前: [apple, banana, cherry, apricot]
「a」始まりを削除後: [banana, cherry]

サンプルコード(Iterableを実装して拡張for文に対応する)

自作のクラスに Iterable を実装することで、拡張for文で使えるようになります。1から指定した数まで順番に返すカウンタークラスを作ってみましょう。

ファイル作成

「Java33」フォルダに Counter.javaIterableTest.java という名前でファイルを作成します

コード入力

Counter.java に以下のコードを入力します。

import java.util.Iterator;

public class Counter implements Iterable<Integer> {
    private final int max;

    public Counter(int max) {
        this.max = max;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            private int current = 1;

            @Override
            public boolean hasNext() {
                return current <= max;
            }

            @Override
            public Integer next() {
                return current++;
            }
        };
    }
}

IterableTest.java に以下のコードを入力します。

public class IterableTest {
    public static void main(String[] args) {
        // 自作のCounterクラスを拡張for文で使う
        Counter counter = new Counter(5);

        System.out.println("--- 1から5まで ---");
        for (int num : counter) {
            System.out.println(num);
        }

        // 何度でも繰り返し使える
        System.out.println("\n--- 2回目 ---");
        for (int num : counter) {
            System.out.print(num + " ");
        }
        System.out.println();

        // 偶数だけ表示
        System.out.println("\n--- 1から10の偶数 ---");
        for (int num : new Counter(10)) {
            if (num % 2 == 0) {
                System.out.print(num + " ");
            }
        }
        System.out.println();
    }
}

コンパイル

javac Counter.java IterableTest.java

実行

java IterableTest

実行結果は次のようになります。

--- 1から5まで ---
1
2
3
4
5

--- 2回目 ---
1 2 3 4 5 

--- 1から10の偶数 ---
2 4 6 8 10

コードの解説

Iterator<String> iterator = names.iterator() でリストのIteratorを取得し、iterator.hasNext() で次の要素があるかを確認しながら iterator.next() で要素を取り出しています。拡張for文はこの動作を自動で行っているだけで、コンパイル後には実質同じコードになります。

ループ中にリストから要素を削除したい場合、拡張for文の中で list.remove() を直接呼ぶと ConcurrentModificationException というエラーが発生します。Iteratorの it.remove() を使うことで、ループ中でも安全に要素を削除できます。

Counter クラスは Iterable<Integer> を実装し、iterator() メソッドの中で無名クラスを使って Iterator<Integer> を返しています。current 変数が1から max まで増えていき、hasNext()false を返したらループが終わります。iterator() メソッドを呼ぶたびに新しいIteratorが作られるため、同じ Counter インスタンスを何度でも拡張for文で使えます。

まとめ

Iterator は「次の要素があるか(hasNext)」「次の要素を取り出す(next)」という2つの操作を提供し、Iterable は「Iterator を返せる(iterator)」ことを示します。拡張for文の裏側はこの仕組みで動いており、自分のクラスに Iterable を実装することで拡張for文に対応させられます。ループ中に要素を削除したい場合はIteratorの remove() を使う点も覚えておきましょう。これまで何気なく使ってきた拡張for文の仕組みを理解することで、Javaのコレクション全体への理解が一層深まります。

2026/07/24(金) ぱんまる

コメント

タイトルとURLをコピーしました