Iterator パターン — Java では `Iterable` 実装 + 拡張 for で終わり
GoF の Iterator パターンを Java で書くときの最小実装。拡張 for 文、Spliterator、Stream との関係まで整理する。

どういう問題を解くパターンか
コレクションの内部構造を隠したまま、順次アクセスの手段だけを公開する パターン。
- 呼び出し側は 「配列なのか連結リストなのかツリーなのか」 を知らずに
hasNext/nextだけで扱える - コレクション側は内部データ構造を自由に差し替えられる
Java では既に言語機能とライブラリに取り込まれています。
java.util.Iterator, Iterable, 拡張 for 文, Spliterator, Stream。
したがって Java 開発者がこのパターンを 「自作する」 必要はほぼありません。
このパターンを学ぶ価値は、Java のコレクション API がどう設計されているかを理解する ことにあります。
いつ使う / 使わない
| 使うべき | 使わないほうがよい |
|---|---|
| 自作コレクションを拡張 for で使いたい | 標準 Collection を使っている |
| 内部構造を外に見せたくない | List#get(i) で十分 |
| 要素を 「遅延評価」 で供給したい | 全件が既にメモリ上にある |
最小実装: 自作コレクションに Iterator を持たせる
データ (ドメインモデル)
public record Person(String name, int age) {}
自作コレクション (Iterable を実装)
public final class PersonList implements Iterable<Person> {
private Person[] persons;
private int size = 0;
public PersonList(int capacity) {
this.persons = new Person[capacity];
}
public void add(Person p) {
if (size == persons.length) {
persons = Arrays.copyOf(persons, persons.length * 2);
}
persons[size++] = p;
}
public int size() { return size; }
@Override
public Iterator<Person> iterator() {
return new Iterator<>() {
private int index = 0;
@Override public boolean hasNext() {
return index < size;
}
@Override public Person next() {
if (!hasNext()) throw new NoSuchElementException();
return persons[index++];
}
};
}
}
呼び出し側: 3 通り
PersonList list = new PersonList(4);
list.add(new Person("Alice", 30));
list.add(new Person("Bob", 25));
// 1. 拡張 for 文 (もっとも読みやすい)
for (Person p : list) {
System.out.println(p.name());
}
// 2. Iterator を直接
Iterator<Person> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next().name());
}
// 3. Stream (list.spliterator() 経由)
StreamSupport.stream(list.spliterator(), false)
.filter(p -> p.age() >= 20)
.map(Person::name)
.forEach(System.out::println);
ポイント: Iterable<T> さえ実装すれば、拡張 for も Stream も使えるようになります。
「GoF Iterator」 を自分で書く意義は、この最小限の実装の中にあります 。
Iterator と Spliterator と Stream の関係
| 用途 | 並列 | |
|---|---|---|
Iterator<T> | 1 要素ずつ順次アクセス | ❌ |
Spliterator<T> | 分割 + 順次 (並列化のための分割アルゴリズム付き) | ✅ |
Stream<T> | 高レベルな関数型パイプライン | ✅ (parallelStream) |
Iterable を実装すると spliterator() のデフォルト実装が内部で iterator() を使って Spliterator を作ってくれるので、Stream もすぐ使えます。
ただし デフォルト Spliterator は並列化効率が低い ので、並列処理を本気で使うなら
Spliterator を自前実装することを検討。
遅延評価 Iterator の例
有限とは限らない (無限列) や巨大すぎてメモリに乗らないデータは、 Iterator で 「必要になった時点で生成」 する実装が自然です。
public final class NaturalNumbers implements Iterable<Integer> {
private final int limit;
public NaturalNumbers(int limit) {
this.limit = limit;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
private int n = 0;
@Override public boolean hasNext() { return n < limit; }
@Override public Integer next() { return ++n; }
};
}
}
Stream で同じことをすると Stream.iterate(1, n -> n + 1).limit(limit) と書けます。
書き味は Stream の方が上。Iterator の自作は、Stream API が使えない制約下か、
極端にホットなループで Stream のオーバーヘッドを嫌う場面に限ります。
落とし穴
1. イテレーション中にコレクションを変更する
多くの Iterator 実装は 「fail-fast」 です。
反復中に元コレクションを add/remove すると ConcurrentModificationException。
安全に削除したいなら Iterator 自身の remove() を使う:
Iterator<Person> it = list.iterator();
while (it.hasNext()) {
if (it.next().age() < 18) {
it.remove(); // OK (この Iterator が削除を主導している)
}
}
または removeIf(Predicate) を使う方が読みやすい。
2. 複数スレッドから同じ Iterator を触る
Iterator はスレッド安全ではありません。
並行環境では CopyOnWriteArrayList の弱整合 Iterator を使うか、
Stream の parallelStream に委ねる。
3. 無限列 Iterator を終端操作なしで Stream に食わせる
IntStream.iterate(1, n -> n + 1) // 無限
.forEach(System.out::println); // 終わらない
必ず .limit(n) や .takeWhile(...) で有限化。
まとめ: GoF Iterator は 「既に Java の言語機能に組み込まれているパターン」 の典型。
自作するなら Iterable<T> を実装するだけで拡張 for / Stream まで一式使えるようになります。
「内部構造を隠して反復だけ公開する」 という思想は現代でも通用し、
API 設計の基礎として理解しておく価値があります。
関連記事
- アルゴリズム·
Builder パターン — 引数 4 つを超えたら迷わず導入する
コンストラクタ引数が多いクラスの可読性を救う Builder。手書き・Lombok・record の併用パターンと、Fluent Builder でよくあるハマり所。
- アルゴリズム·
AbstractFactory パターン — 「関連するオブジェクト群」を一式で切り替える
Factory Method との違いを 1 分で整理し、OS 別 UI / DB 別 Repository / テナント別実装など、実務での使いどころを示す。
- アルゴリズム·
TemplateMethod パターン — Spring JdbcTemplate に見る典型実装
処理の骨格は親、差分は子に書き分ける Template Method。Spring の `*Template` 系の設計思想と、Strategy / Decorator との使い分け。