1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GoFデザインパターン】Iterator(イテレーター)パターンをJavaで理解する

1
Last updated at Posted at 2026-09-24

はじめに

本記事は、GoFデザインパターン解説シリーズの1つです。

今回は、振る舞いに関するパターンの中のIterator(イテレーター)パターンを解説します。

Javaを使っていればfor-each文で毎日のように無意識に恩恵を受けている、非常に身近なパターンです。

解決したい課題

配列、リスト、木構造、ハッシュマップなど、コレクション(データの集合)には様々な内部構造があります。

もし、コレクションの要素を1つずつ処理するコードを書くたびに、その内部構造(配列なのか、連結リストなのか、木構造なのか)を呼び出し側が意識しなければならないとすると、コレクションの実装を後から変更した際に、それを利用しているすべてのコードに影響が及んでしまいます。

Iteratorパターンは、「集合の要素を順番に取り出す」という操作を、専用のIteratorオブジェクトに切り出すことで、この問題を解決します。

呼び出し側は、コレクションの内部構造を一切知らなくても、共通の方法で要素を1つずつ走査できます。

image.png

クラス構成

  • Iterator(イテレーター): 要素を1つずつ取り出すための共通インターフェース(「次の要素があるか」「次の要素を取得する」)
  • ConcreteIterator(具体的なイテレーター): 特定のコレクションに対応した、具体的なIteratorの実装
  • Aggregate(集合): Iteratorを生成するメソッドを持つ、コレクション側の共通インターフェース
  • ConcreteAggregate(具体的な集合): 実際にデータを保持する、具体的なコレクションクラス

image.png

Javaでの実装例

「本棚(BookShelf)」から「本(Book)」を1冊ずつ取り出す例を、あえてJava標準のIterable/Iteratorを使わず、パターンの構造がわかる形で自作実装してみます。

Iterator:共通インターフェース

public interface MyIterator<T> {
    boolean hasNext();
    T next();
}

Aggregate:集合(コレクション)側の共通インターフェース

public interface MyAggregate<T> {
    MyIterator<T> createIterator();
}

要素となるBookクラス

public class Book {
    private final String title;

    public Book(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
}

ConcreteAggregate:具体的なコレクションクラス

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

public class BookShelf implements MyAggregate<Book> {
    private final List<Book> books = new ArrayList<>();

    public void addBook(Book book) {
        books.add(book);
    }

    public Book getBookAt(int index) {
        return books.get(index);
    }

    public int getBookCount() {
        return books.size();
    }

    @Override
    public MyIterator<Book> createIterator() {
        return new BookShelfIterator(this);
    }
}

ConcreteIterator:BookShelf専用のイテレーター

public class BookShelfIterator implements MyIterator<Book> {
    private final BookShelf bookShelf;
    private int currentIndex = 0;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
    }

    @Override
    public boolean hasNext() {
        return currentIndex < bookShelf.getBookCount();
    }

    @Override
    public Book next() {
        Book book = bookShelf.getBookAt(currentIndex);
        currentIndex++;
        return book;
    }
}

利用側のコード

public class Main {
    public static void main(String[] args) {
        BookShelf bookShelf = new BookShelf();
        bookShelf.addBook(new Book("リーダブルコード"));
        bookShelf.addBook(new Book("デザインパターン入門"));
        bookShelf.addBook(new Book("Effective Java"));

        // 利用側は、BookShelfの内部がListであることを知らなくても走査できる
        MyIterator<Book> iterator = bookShelf.createIterator();
        while (iterator.hasNext()) {
            Book book = iterator.next();
            System.out.println(book.getTitle());
        }
    }
}

実行結果:

リーダブルコード
デザインパターン入門
Effective Java

image.png

利用側のコードは、BookShelfが内部でArrayListを使っているのか、配列を使っているのか、あるいは連結リストなのかを一切知りません。

hasNext()とnext()という共通のインターフェースだけを使って、走査を行っています。

将来BookShelfの内部実装をArrayListから別のデータ構造に変更しても、BookShelfIteratorの内部だけを修正すればよく、利用側のコードには影響しません。

Java標準ライブラリでの実例

実は、この考え方はJava言語自体に深く組み込まれています。

java.util.Iteratorインターフェースとjava.lang.Iterableインターフェースがそれに当たります。

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

List<String> titles = new ArrayList<>(List.of("リーダブルコード", "Effective Java"));

// Iterableを実装したクラスなら、for-each文が使える(内部的にはIteratorが使われている)
for (String title : titles) {
    System.out.println(title);
}

// 上記のfor-each文は、実質的に以下のコードと同じ
Iterator<String> it = titles.iterator();
while (it.hasNext()) {
    String title = it.next();
    System.out.println(title);
}

自作クラスにIterable<T>を実装し、iterator()メソッドを提供すれば、そのクラスもfor-each文で走査できるようになります。

これは、まさにIteratorパターンをJava言語のシンタックスシュガー(糖衣構文)として組み込んだものと言えます。

使用場面

  • 自作のコレクションクラスを、利用側に統一的な方法で走査させたい場合
  • コレクションの内部データ構造(配列・リスト・木構造など)を隠蔽し、将来の実装変更の影響を抑えたい場合
  • 同じコレクションに対して、異なる走査方法(順方向・逆方向・条件を満たす要素だけ、等)を複数提供したい場合

まとめ

項目 内容
解決する課題 コレクションの内部構造を、利用側が意識せずに要素を走査したい
実現方法 「要素を順番に取り出す」処理を、専用のIteratorオブジェクトに切り出す
メリット コレクションの内部実装を変更しても、利用側のコードに影響しない
Java標準 Iterable/Iteratorインターフェースとして言語レベルで組み込まれている(for-each文)

おすすめ書籍

GoFの23パターンをより深く学びたい方には、以下の書籍がおすすめです。
Java言語のサンプルコードとUMLを使い、初心者にもわかりやすく解説されています。

Java言語で学ぶデザインパターン入門 第3版 [ 結城 浩 ]

※本リンクはアフィリエイトリンクを含みます。

参考

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?