Java拡張for文(for-each文)完全ガイド|基本構文・使い方・従来for文との違いと注意点

1. はじめに

Javaを学習していると、「拡張for文」や「for-each文」というキーワードを目にすることが多くなります。従来のfor文に慣れている方にとっては、「何が違うの?」「どんな時に使うべき?」といった疑問を抱くこともあるでしょう。

本記事では、Javaの拡張for文(for-each文)について、基本から応用、従来のfor文との違い、よくあるエラーや注意点、そして実務で役立つFAQまでを丁寧に解説します。
拡張for文は、配列やコレクションなど複数のデータを扱う際に、コードをシンプルかつ分かりやすく記述できる便利な機能です。Java初学者から、実際に業務でJavaを利用している中級者まで、幅広い読者の「なぜ?」「どうやって?」に答えられる内容を目指しています。

この記事を読むことで、拡張for文の使い方だけでなく、従来のfor文との使い分けや応用的な書き方まで、体系的に理解できるようになります。これからJavaのループ処理を効率化したい方や、コードの可読性を向上させたい方は、ぜひ参考にしてください。

2. 拡張for文(for-each文)の概要

拡張for文(for-each文)は、Java 5(JDK 1.5)から導入された新しい形式の繰り返し処理です。英語では「Enhanced for statement」や「for-each loop」と呼ばれ、従来のfor文に比べて、コードをより簡潔に記述できる点が大きな特徴です。

この文法は、主に配列やコレクション(ListやSetなど)といった、複数の要素を持つデータ構造を順番に処理したい場合に活用されます。従来のfor文では、インデックス変数を自分で用意し、要素数や境界条件を意識する必要がありましたが、拡張for文ではその手間が不要になります。

例えば、拡張for文を使うことで「配列の全要素を一つずつ取り出す」「リスト内のデータを順番に処理する」といった作業が、非常に直感的かつ安全に実現できます。また、可読性が高まり、バグも起きにくくなるため、近年のJavaプログラムでは標準的な書き方として広く採用されています。

要点として、拡張for文(for-each文)の特徴は以下の通りです。

  • Java 5以降で利用可能
  • 配列やコレクションの全要素に対して、簡単な構文でアクセスできる
  • コードが短くなり、可読性も向上する
  • 境界エラーやインデックスのミスを防ぎやすい

このような理由から、複数の要素をまとめて扱う場面では、拡張for文の利用が強く推奨されています。

3. 拡張for文の基本構文と使い方

拡張for文(for-each文)は、配列やコレクションの全ての要素を順番に処理したい場合に非常に便利な構文です。基本的な書き方は、下記のようになります。

for (データ型 変数名 : 配列またはコレクション) {
    // 各要素に対して行う処理
}

配列に対する拡張for文の例

例えば、int型の配列を順に出力したい場合、拡張for文を使うと以下のように書くことができます。

int[] numbers = {1, 2, 3, 4, 5};

for (int num : numbers) {
    System.out.println(num);
}

この例では、配列numbersの各要素が順番にnumに代入され、System.out.println(num);の部分で出力されます。従来のfor文に比べて、インデックス変数やループの範囲指定が不要なため、コードが非常にシンプルです。

List(リスト)などコレクションに対する例

拡張for文は、配列だけでなくListやSetなどのコレクションにも利用できます。例えば、String型のリストをすべて出力したい場合は以下のようになります。

List<String> names = Arrays.asList("田中", "佐藤", "鈴木");

for (String name : names) {
    System.out.println(name);
}

この例でも、リストnamesの各要素が順番にnameに代入されて処理されます。
ListやSetなど、Iterableインタフェースを実装しているコレクションであれば、同じように拡張for文を使ってループ処理が可能です。

サンプル出力

上記の2つの例では、次のような出力になります。

1
2
3
4
5

または

田中
佐藤
鈴木

このように拡張for文は、複雑なループ条件やインデックスを意識せずに、全ての要素を順番に処理したい時に最適です。

4. 従来のfor文との違いと比較

Javaには「従来のfor文(インデックス付きfor文)」と「拡張for文(for-each文)」の2種類の繰り返し構文があります。どちらも繰り返し処理を行うために使われますが、それぞれに特徴や向き不向きがあります。

構文の違い

従来のfor文(インデックス付きfor文)

for (int i = 0; i < 配列.length; i++) {
    System.out.println(配列[i]);
}

インデックスiを使って配列やリストの各要素にアクセスする形式です。
要素番号(インデックス)を直接操作できるので、任意の位置の要素にアクセスしたり、部分的な処理や逆順処理も柔軟に記述できます。

拡張for文(for-each文)

for (データ型 変数 : 配列またはコレクション) {
    System.out.println(変数);
}

各要素を順番に自動で変数に代入して処理します。
インデックスを意識する必要がなく、コードを簡潔に書けます。

比較表:拡張for文 vs 従来for文

項目拡張for文従来for文
構文のシンプルさ◎(簡単・直感的)△(やや複雑)
インデックス操作×(不可)◎(自由に操作可能)
要素の削除×(非推奨)△(条件付きで可)
全要素処理
逆順処理×(不可)◎(記述可能)
要素のスキップ×(難しい)◎(柔軟に可能)

どちらを使うべきか?選び方のポイント

拡張for文が向いているケース

  • 配列やコレクションの全要素を順番に処理したい
  • コードを簡潔に書きたい
  • 要素番号や逆順処理が不要な場合

従来のfor文が向いているケース

  • インデックス番号を利用したい(例:特定位置へのアクセス、逆順処理、部分的なスキップなど)
  • 要素の追加・削除や、イテレータによる複雑な操作が必要な場合

両者の違いを正しく理解し、用途に応じて使い分けることが、Javaで効率的かつ安全なループ処理を実現するポイントです。

5. 拡張for文の応用例

拡張for文(for-each文)は、配列やリストなど基本的なコレクション以外にも、さまざまなデータ構造や用途で活用できます。ここでは、実務でよく使われる応用例をいくつか紹介します。

Mapのループ処理

Mapは、KeyとValueのペアでデータを管理するコレクションですが、拡張for文を使う場合はentrySet()を利用します。
以下は、Mapのすべての要素を出力する例です。

Map<String, Integer> scores = new HashMap<>();
scores.put("田中", 80);
scores.put("佐藤", 90);
scores.put("鈴木", 75);

for (Map.Entry<String, Integer> entry : scores.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

このように、entrySet()で各エントリー(Key-Valueペア)を1つずつ取り出して処理します。

二次元配列のループ

二次元配列など、多次元配列も拡張for文で簡単に処理できます。
例えば、整数の二次元配列をすべて出力する場合は次のように書けます。

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

for (int[] row : matrix) {
    for (int num : row) {
        System.out.print(num + " ");
    }
    System.out.println();
}

この例では、外側のループで各行(1次元配列)を取り出し、内側のループで行の各要素を出力しています。

オブジェクト配列やリストのループ

拡張for文は、クラス型の配列やリストにも利用できます。例えば、Personクラスのオブジェクトを配列やリストで管理し、全員の名前を表示することも可能です。

class Person {
    String name;
    Person(String name) {
        this.name = name;
    }
}

Person[] people = {
    new Person("田中"),
    new Person("佐藤"),
    new Person("鈴木")
};

for (Person person : people) {
    System.out.println(person.name);
}

Setや他のコレクションへの応用

Setなど、順序や重複のないコレクションにも、同じように拡張for文を使うことができます。
たとえば、重複を許さないSet<String>の全要素を出力する場合は以下の通りです。

Set<String> fruits = new HashSet<>(Arrays.asList("リンゴ", "バナナ", "オレンジ"));

for (String fruit : fruits) {
    System.out.println(fruit);
}

このように拡張for文は、Javaが提供するほとんどのコレクションや配列、さらにはオブジェクトの集合にも柔軟に対応できます。

6. 拡張for文の注意点・使うべきでないケース

拡張for文は非常に便利な構文ですが、すべての状況で最適というわけではありません。ここでは、拡張for文を利用する際の注意点や、使わないほうが良いケースについて解説します。

インデックスが必要な場合

拡張for文では、ループ内で現在処理している要素のインデックス(番号)を取得できません。そのため、「偶数番目の要素だけ処理したい」「特定の範囲だけアクセスしたい」など、要素の位置を利用した処理には従来のfor文が適しています。

例:従来のfor文でインデックスを利用する場合

int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
    if (i % 2 == 0) {
        System.out.println(numbers[i]);
    }
}

要素の削除や追加を伴う場合

拡張for文でループ中にコレクションから要素を削除・追加すると、ConcurrentModificationExceptionが発生する可能性があります。
要素の削除や追加を行う場合は、Iteratorを利用したループが推奨されます。

例:Iteratorを使って要素を削除する場合

List<String> names = new ArrayList<>(Arrays.asList("田中", "佐藤", "鈴木"));
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("佐藤")) {
        iterator.remove();
    }
}

拡張for文で同じことをするとエラーになるので注意が必要です。

nullや空の配列・コレクションに対する対処

拡張for文でnullの配列やコレクションを処理しようとすると、NullPointerExceptionが発生します。
ループ前にnullチェックや、空でないことの確認を行うようにしましょう。

例:nullチェックの実装例

int[] numbers = null;
if (numbers != null) {
    for (int num : numbers) {
        System.out.println(num);
    }
}

逆順処理や部分的なスキップ

拡張for文は、必ず最初の要素から最後の要素まで順番に処理します。逆順で処理したい場合や、特定の条件で要素をスキップしたい場合も従来のfor文が適しています。

このように、拡張for文は「すべての要素を順番に処理したい」ケースで特に威力を発揮しますが、インデックスを活用する複雑な処理や、要素の追加・削除が必要な場合には他のループ構文を使うのが安全です。

7. よくあるエラー・トラブルシューティング

拡張for文(for-each文)は安全でシンプルな構文ですが、使い方を誤ると思わぬエラーやバグに遭遇することがあります。ここでは、現場でよく起きるエラーとその対策について解説します。

NullPointerException

拡張for文でnullの配列やコレクションを処理しようとすると、NullPointerExceptionが発生します。
これは配列やコレクションが生成されていない(または初期化されていない)場合によく起こります。

例:エラーになるコード

List<String> names = null;
for (String name : names) { // ← NullPointerException
    System.out.println(name);
}

対策:nullチェックを追加する

List<String> names = null;
if (names != null) {
    for (String name : names) {
        System.out.println(name);
    }
}

もしくは、コレクションを初期化してから処理を始めるのが安全です。

要素削除によるConcurrentModificationException

拡張for文のループ中に、対象のコレクションから要素を削除・追加しようとすると、ConcurrentModificationExceptionという例外が発生します。
これはJavaのコレクションが安全性を確保するための仕組みですが、初心者がつまずきやすいポイントです。

例:エラーになるコード

List<String> names = new ArrayList<>(Arrays.asList("田中", "佐藤", "鈴木"));
for (String name : names) {
    if (name.equals("佐藤")) {
        names.remove(name); // ← ConcurrentModificationException
    }
}

対策:Iteratorを使う

Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("佐藤")) {
        iterator.remove(); // これは安全に削除可能
    }
}

配列やコレクションのサイズ変更

拡張for文の内部では、最初に全要素を把握してループを開始します。そのため、ループ中にサイズが変わるような操作(追加や削除)をすると正しく動作しなくなることがあります。
特に、配列のサイズは固定のため、ループ中に変更すること自体ができません。

型の不一致エラー

拡張for文では、「データ型 変数名 : 配列やコレクション」と宣言しますが、指定するデータ型が実際の配列やコレクションの型と一致していないとコンパイルエラーになります。

例:型の不一致でエラーになる場合

List<Integer> numbers = Arrays.asList(1, 2, 3);
// for (String num : numbers) { ... } // ← コンパイルエラー
for (int num : numbers) { // もしくは Integer num : numbers
    System.out.println(num);
}

拡張for文は正しく使えば強力なツールですが、こうしたエラーに注意することで、より安全でバグの少ないプログラムを書くことができます。

8. まとめ

拡張for文(for-each文)は、Javaで配列やコレクションを簡単かつ安全に扱うための便利な構文です。従来のfor文と比べてコードが短くなり、可読性も向上するため、多くの場面で標準的に利用されています。

拡張for文を使うことで、配列やコレクションの全要素を順番に処理したい場合には、特に威力を発揮します。構文がシンプルなので、ループの範囲やインデックスを意識することなく、ミスの少ないプログラムが書ける点が大きなメリットです。

一方で、インデックスを利用したい場合や、要素の削除・追加、逆順処理やスキップが必要な場面では、従来のfor文やIteratorを使う方が適切です。拡張for文の仕組みや制約を理解した上で、状況に応じて最適なループ構文を選択しましょう。

本記事では、拡張for文の基礎から応用、従来for文との違い、注意点やエラー対策までを解説してきました。
これらを踏まえ、用途に合ったループ処理を選ぶことで、Javaのプログラム開発がより効率的で堅牢なものになります。

9. よくある質問(FAQ)

Q1. 拡張for文でインデックスを取得する方法はありますか?
A1. 拡張for文ではインデックス(要素番号)を直接取得することはできません。インデックスが必要な場合は、従来のfor文(for (int i = 0; i < 配列.length; i++) など)を使うか、別途カウンタ変数を用意して管理する方法があります。ただし、インデックスの操作が必要な場面では無理に拡張for文を使わない方がシンプルです。

Q2. 拡張for文でループ中に要素の削除や追加はできますか?
A2. 拡張for文を使ったループ中にコレクションから要素の削除や追加を行うと、ConcurrentModificationExceptionが発生する可能性があります。安全に要素を削除したい場合は、Iteratorを使う方法がおすすめです。

Q3. 拡張for文はどのようなデータ構造で利用できますか?
A3. 拡張for文は、配列と「Iterable」インタフェースを実装したコレクション(List、Setなど)で利用できます。Mapは直接ループできませんが、entrySet()keySet()values()などを使えば拡張for文で処理できます。

Q4. Mapで拡張for文を使うおすすめの方法は?
A4. Mapの場合は、for (Map.Entry<K, V> entry : map.entrySet()) の形式が一般的です。これにより、KeyとValueの両方を簡単に取得できます。Keyだけ、Valueだけを処理したい場合は、それぞれkeySet()values()を使うこともできます。

Q5. 拡張for文はパフォーマンス面で従来のfor文より遅いのでしょうか?
A5. 一般的な用途では、拡張for文と従来のfor文で大きなパフォーマンス差はありません。大量データや高頻度の処理が必要な場合は微差が出ることもありますが、ほとんどの開発現場では可読性・安全性を優先し、拡張for文が積極的に利用されています。

Q6. 拡張for文はネスト(入れ子)にして使えますか?
A6. はい、拡張for文は多次元配列やコレクションの入れ子に対しても使用できます。外側・内側ともにfor-each文で記述できるため、二次元配列などの処理もシンプルに書くことができます。

Q7. 拡張for文とIteratorの使い分けはどうすれば良いですか?
A7. 要素の削除や追加などコレクションの構造を変更したい場合はIteratorを使い、単純に全要素を順に処理したい場合は拡張for文を使うのが一般的です。

10. 参考リンク・関連記事

公式ドキュメント・基礎学習に役立つ外部サイト

学習をさらに深めたい方へおすすめ書籍

本記事をきっかけに、Javaの繰り返し構文やコレクションの扱い方についてさらに理解を深めてみてください。