カテゴリ: Java 更新日: 2026/02/04

Java IterableとIteratorの基本|ループ処理の仕組みを理解する

Java IterableとIteratorの基本|ループ処理の仕組みを理解する
Java IterableとIteratorの基本|ループ処理の仕組みを理解する

先生と生徒の会話形式で理解しよう

生徒

「Javaのリストを処理するとき、いつも拡張for文を使っているのですが、内部でどう動いているのか気になります。」

先生

「それは非常に重要な視点ですね。実は、拡張for文の裏側では、Iterable(アイテラブル)とIterator(アイテレータ)という仕組みが動いているんですよ。」

生徒

「アイテラブル?アイテレータ?なんだか呪文みたいで難しそうです…。」

先生

「大丈夫ですよ。これらは『順番に要素を取り出すためのルール』に過ぎません。この仕組みを知ると、データの削除やカスタムな繰り返し処理も自由自在になります。さっそく、詳しく見ていきましょう!」

1. IterableとIteratorの役割とは?

1. IterableとIteratorの役割とは?
1. IterableとIteratorの役割とは?

Javaのコレクションフレームワークを使いこなす上で、避けて通れないのがIterableインターフェースとIteratorインターフェースです。これらは、ArrayListやHashSetなどのコレクションに含まれる要素を、一つずつ順番に取り出すための共通の仕組みを提供しています。

簡単に言うと、Iterableは「私は順番に取り出すことができますよ」という能力を示す看板のようなもので、Iteratorは「実際に要素を一つずつ取り出す作業員」のような役割を担っています。私たちが普段何気なく使っている拡張for文は、実はこの作業員であるIteratorを呼び出して、要素を一つずつ届けてもらっているのです。

なぜこの二つが必要なのでしょうか。それは、データの構造がArrayListのような配列形式であっても、HashSetのような集合形式であっても、同じ書き方で中身をループ処理できるようにするためです。この「抽象化」という考え方こそが、Javaという言語の強力な武器の一つになっています。

2. Iteratorを使った基本的なループ処理

2. Iteratorを使った基本的なループ処理
2. Iteratorを使った基本的なループ処理

まずは、拡張for文を使わずに、Iteratorを直接操作してリストの要素を取り出す方法を見てみましょう。これがすべてのループ処理の原点となります。

Iteratorを使うには、まずコレクションのiteratorメソッドを呼び出します。これにより、Iteratorオブジェクトが生成されます。このオブジェクトには、次の要素があるかを確認するhasNextメソッドと、次の要素を実際に取得するnextメソッドが備わっています。


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

public class IteratorBasicExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");

        // Iteratorを取得する
        Iterator<String> it = fruits.iterator();

        // 次の要素がある限り繰り返す
        while (it.hasNext()) {
            String fruit = it.next();
            System.out.println("果物の名前: " + fruit);
        }
    }
}

果物の名前: Apple
果物の名前: Banana
果物の名前: Orange

このプログラムでは、while文の中でhasNextメソッドがtrueを返す間、nextメソッドを呼び出し続けています。nextメソッドを呼ぶと、Iteratorは現在の位置から一つ進み、その場所にあるデータを返します。この一連の流れが、Javaにおける反復処理の基本形です。

3. 拡張for文とIterableの関係性

3. 拡張for文とIterableの関係性
3. 拡張for文とIterableの関係性

Java 5から導入された「拡張for文」は、先ほどのIteratorを使った複雑な記述を簡潔に書けるようにしたものです。実は、拡張for文を使える条件は「そのオブジェクトがIterableインターフェースを実装していること」だけです。

コンパイラは、拡張for文を見つけると、自動的にIteratorを使ったコードに書き換えて実行します。つまり、私たちが書くコードはシンプルになりますが、コンピュータの内部では依然としてIteratorがコツコツと働いているのです。Iterableインターフェースを実装しているクラスであれば、ArrayListだけでなく、LinkedListやTreeSetなど、どんなデータ構造でも同じ書き方でループができるようになります。これはオブジェクト指向プログラミングにおける「ポリモーフィズム」の素晴らしい例と言えるでしょう。

4. 反復処理中に安全に要素を削除する方法

4. 反復処理中に安全に要素を削除する方法
4. 反復処理中に安全に要素を削除する方法

Iteratorを直接使う最大のメリットの一つは、ループの途中で安全に要素を削除できることです。通常のfor文や拡張for文の中で「リストから特定の条件に合うものを消したい」と考えて削除処理を行うと、ConcurrentModificationExceptionというエラーが発生することがあります。

これは、反復処理中に元のリストの構造が勝手に変わってしまうことを防ぐための安全装置です。しかし、Iteratorのremoveメソッドを使えば、現在指している要素を安全に削除することが可能です。


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

public class IteratorRemoveExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(25);
        numbers.add(30);
        numbers.add(45);

        Iterator<Integer> it = numbers.iterator();
        while (it.hasNext()) {
            Integer num = it.next();
            // 20より大きい数値を取り除く
            if (num > 20) {
                it.remove();
            }
        }

        System.out.println("削除後のリスト: " + numbers);
    }
}

削除後のリスト: [10]

このように、特定の条件に合致するデータをリストから一掃したい場合には、Iteratorの使用が推奨されます。拡張for文では不可能な操作ができる点が、Iteratorの強みです。

5. 逆順やランダムなアクセスはできる?

5. 逆順やランダムなアクセスはできる?
5. 逆順やランダムなアクセスはできる?

Iteratorは基本的に一方通行です。先頭から末尾に向かって一つずつ進むことしかできません。しかし、Listインターフェース専用のIteratorとして「ListIterator」というものが存在します。これを使うと、前方向に進んだり、要素を途中で追加したり、現在の要素を書き換えたりといった高度な操作が可能になります。


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

public class ListIteratorExample {
    public static void main(String[] args) {
        List<String> languages = new ArrayList<>();
        languages.add("Java");
        languages.add("Python");
        languages.add("C++");

        ListIterator<String> lit = languages.listIterator(languages.size());

        System.out.println("逆順に表示します:");
        while (lit.hasPrevious()) {
            String lang = lit.previous();
            System.out.println(lang);
        }
    }
}

逆順に表示します:
C++
Python
Java

このコードでは、リストの末尾の位置からIteratorを開始し、hasPreviousメソッドとpreviousメソッドを使って逆方向に辿っています。通常のIteratorよりも多機能ですが、List以外のコレクション(Setなど)では使えないという制約がある点には注意が必要です。

6. 関数型プログラミングとforEachメソッド

6. 関数型プログラミングとforEachメソッド
6. 関数型プログラミングとforEachメソッド

Java 8以降、IterableインターフェースにはforEachメソッドが追加されました。これにより、ラムダ式を使ってさらに簡潔にループを記述できるようになりました。Iteratorを意識せずに書けるため、現代のJava開発ではこのスタイルが非常に好まれます。


import java.util.Arrays;
import java.util.List;

public class LambdaForEachExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("田中", "佐藤", "鈴木");

        // ラムダ式を使った簡潔なループ
        names.forEach(name -> System.out.println(name + "さん、こんにちは!"));

        // メソッド参照を使った記述
        // names.forEach(System.out::println);
    }
}

田中さん、こんにちは!
佐藤さん、こんにちは!
鈴木さん、こんにちは!

内部的にはやはりIterableの仕組みが使われていますが、記述が一行で済むため可読性が向上します。用途に合わせて、拡張for文、Iterator、forEachメソッドを使い分けるのがJavaマスターへの近道です。

7. Iteratorの内部状態と注意点

7. Iteratorの内部状態と注意点
7. Iteratorの内部状態と注意点

Iteratorを使用する際に初心者が陥りやすい罠がいくつかあります。まず、nextメソッドは一度呼ぶとカーソルが一つ進んでしまうということです。一つのループ内で何度もnextを呼ぶと、要素をスキップしてしまう原因になります。値を使いたい場合は、必ず一度変数に代入してから使用するようにしましょう。

また、Iteratorは一度使い切ると再利用できません。もう一度最初から要素を取り出したい場合は、再度iteratorメソッドを呼び出して新しいIteratorオブジェクトを生成する必要があります。これは、Iteratorが現在の読み取り位置という「状態」を保持しているためです。この性質を理解しておかないと、予期せぬバグを招くことになります。Iteratorは「一回限りの使い捨て作業員」だと覚えておくと分かりやすいでしょう。

8. コレクション以外でのIterableの活用

8. コレクション以外でのIterableの活用
8. コレクション以外でのIterableの活用

実は、Iterableインターフェースは自分で作るクラスに実装することも可能です。例えば、大量のログファイルを一行ずつ読み込むクラスや、数値を特定の規則で生成し続けるクラスをIterableにすれば、それらを拡張for文で回せるようになります。これは「カスタムな繰り返し」を定義できることを意味します。

Javaの標準ライブラリ以外でも、データベースの結果セットをラップしてIterableにするライブラリなどが多く存在します。このように、IterableとIteratorはJava言語における「繰り返しの標準プロトコル」としての地位を確立しています。この概念を深く理解することは、単にループを書けるようになるだけでなく、ライブラリの設計思想を理解し、より高度なプログラミングを行うための土台となるのです。

カテゴリの一覧へ
新着記事
New1
Quarkus
Quarkusのフォーム認証を基礎から解説!初心者向けセキュリティ入門ガイド
New2
Micronaut
MicronautプロジェクトをGradleで管理する基礎!build.gradleの役割を解説
New3
Micronaut
LinuxでMicronautをセットアップする方法!パッケージ管理とGradle連携
New4
Java
Javaのswitch文を徹底解説!case・defaultの書き方と実例まとめ
人気記事
No.1
Java&Spring記事人気No1
Quarkus
Quarkusプロジェクト構成の基本を完全解説!初心者でも迷わない「どこに何を書くか」ガイド
No.2
Java&Spring記事人気No2
Quarkus
QuarkusとMicronautとHelidonを徹底比較!軽量Javaフレームワークの違いを初心者向けに解説
No.3
Java&Spring記事人気No3
Quarkus
Quarkusのセキュリティ基礎を初心者でもわかるように解説!
No.4
Java&Spring記事人気No4
Quarkus
Quarkusの開発環境構築で躓きやすいポイントを完全解説!初心者でも安心して始めるためのチェックガイド
No.5
Java&Spring記事人気No5
Quarkus
Quarkusでマイクロサービス開発が加速する理由を徹底解説!Java初心者でも分かるクラウドネイティブ
No.6
Java&Spring記事人気No6
Micronaut
MicronautとSpring Bootの違いとは?アーキテクチャ比較で速さの秘密を理解する
No.7
Java&Spring記事人気No7
Micronaut
Micronautのアプリケーション起動が速い理由を初心者向けに解説
No.8
Java&Spring記事人気No8
Java
Javaのboolean型の使い方を完全ガイド!真偽値と条件分岐の基本