MEDIA

メディア

  1. TOP
  2. メディア
  3. プログラミング
  4. Java拡張for文を完全解説|基本構文・配列/コレクション処理・例外回避まで総まとめ

Java拡張for文を完全解説|基本構文・配列/コレクション処理・例外回避まで総まとめ

Javaで配列やコレクションを扱うとき、拡張for文(Enhanced for loop)はもっとも使用頻度の高い構文のひとつです。しかし、拡張for文は簡潔で便利な一方で、内部仕様や制限を正しく理解しないまま使われるケースも多く、開発現場では思わぬ不具合を生むことがあります。

そのため、本記事では拡張for文の仕組み・具体的な書き方・通常for文との明確な使い分け・注意点を体系的に解説します。「なんとなく使っている」という状態から脱却し、実務で自信を持って使い分けられるレベルを目指しましょう。for文全般の使い分けについてはJavaのnew演算子とは?インスタンス生成の仕組み・メモリ構造・NPE回避まで徹底解説もあわせてご覧ください。

拡張for文とは何か|導入された背景と目的

拡張for文の基本概念と通常for文との比較を示す図解

まず、拡張for文は配列やコレクションの反復処理を、よりシンプルかつ安全に書くために設計された構文です。従来のfor文では「初期化・条件式・カウンタ更新」という3つのステップが必要でした。しかし、拡張for文では要素を直接受け取るだけで反復処理が完了します。

さらに、この構文はJava 5(2004年)でGenerics(総称型)やAutoBoxingと共に導入されました。Javaの型安全性と可読性を大きく向上させる役割を担ってきた、歴史のある構文です。

なぜ通常のfor文では不十分だったのか

一方で、通常のfor文は「柔軟性が高い」反面、単純なループ処理においては次のような課題がありました。インデックス(i)の管理が煩雑で、範囲外アクセスなどのバグが生まれやすいこと、Iteratorを手動で扱うコードは記述が長く読み手にとってノイズになること、リストから要素を取り出すlist.get(i)のような定型コードが繰り返し発生することが主な問題でした。

こうした背景から、要素を「先頭から順番に取り出すだけ」の処理を簡潔に書くための専用構文として、拡張for文が定着しました。したがって、シンプルな読み取り処理では積極的に拡張for文を選ぶことがコードの品質向上につながります。

拡張for文の基本構文と使い方|配列・List・Set・Mapの実例

Java拡張for文の配列・List・Set・Mapに対する使い方の実例図解

拡張for文の基本構文は非常にシンプルで、直感的に記述できます。

for (要素の型 変数名 : 配列またはコレクション) {
    // 要素を使った処理
}

このように、インデックスやイテレータを意識することなく、変数に要素が順番に代入されていきます。コードが短くなるだけでなく、インデックス操作による凡ミスも防げます。

配列の基本パターン

配列を処理する場合、もっとも短い記述で全要素にアクセスできます。

String[] names = {"田中", "鈴木", "佐藤"};

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

しかし、配列の「一部だけ(例:先頭から3つ)」を扱いたい場合や、「現在の要素が何番目か」を知る必要がある場合は、通常のfor文の方が適しています。配列に対する拡張for文は、コンパイル時に内部的にインデックスベースのループへ変換され、効率的に動作します。詳細な仕様はJava言語仕様 JLS 14.14.2で確認できます。

List・Setの処理

コレクションフレームワーク(List・Setなど)も同様に処理できます。

List nums = List.of(10, 20, 30);

for (int n : nums) { // AutoUnboxingによりInteger→intへ自動変換
    System.out.println(n);
}

Listは格納された順番通りに要素が取り出されます。一方、Setは実装クラス(HashSetなど)の仕様に応じた順序で要素が取り出されるため、順序が保証されない点に注意が必要です。

Mapの例(entrySetを活用する)

Mapは直接ループさせることができません。そのため、キーと値を同時に扱いたい場合は、entrySet()を使うのがベストプラクティスです。

Map<String, Integer> scores = Map.of("A", 90, "B", 80);

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

keySet()values()で回すことも可能です。しかし、キーと値の両方が必要な場面ではentrySet()の方が効率的です。一度のアクセスでキーと値を取得できるため、余計なマップ参照が発生しません。詳しい仕様はOracle公式Javaドキュメント(Map解説)も参照してください。

通常for文との違いと「使い分け」戦略

拡張for文と通常for文の使い分け判断基準を示す比較図

拡張for文は非常に便利ですが、万能ではありません。したがって、実務では明確な使い分けが必要です。判断のポイントは「インデックスが必要か」「ループ中に要素を変更するか」の2点です。

インデックスを扱いたい場合は通常のfor文

拡張for文には「現在のインデックス」を取得する機能がありません。そのため、次のようなケースでは通常のfor文が推奨されます。「◯番目のデータ」というログを出力したいインデックス参照、「偶数番目のみ」「後ろから逆順に」といった特定要素だけの処理、途中でカウンタを飛ばしたり戻したりする特殊なループ制御、ソートロジックの実装など要素の入れ替えが必要な場合が該当します。

コレクション変更が必要な場合の判断基準

さらに、ループしながらコレクションの要素を「削除・追加」したい場合、拡張for文は不向きです。拡張for文でのループ中の構造変更(add/remove)は原則禁止で、例外が発生します。一方、Iteratorを使う場合はremove()メソッドで安全に削除できます。通常for文ではインデックスを操作しながら削除できますが、バグを生みやすいため注意が必要です。このように、単純な読み取り以外では、拡張for文以外の選択肢を検討する必要があります。

拡張for文の注意点と安全な書き方

ConcurrentModificationExceptionの発生パターンと安全な削除方法の図解

拡張for文を使用する際、もっとも注意すべきなのがConcurrentModificationException(並行変更例外)です。コレクションに対する拡張for文は、内部的にIteratorを使用して動作します。そのため、Iteratorを経由せずにリストの要素を直接削除(list.remove())すると、整合性が取れなくなり例外がスローされます。

例えば、拡張for文の中でlist.remove(element)を呼ぶコードを書いてしまうのが典型的な失敗パターンです。実行時まではエラーが見えないため、テストで気づかないまま本番に出てしまうこともあります。したがって、削除が必要なときは最初から安全な方法を選ぶことが重要です。

ConcurrentModificationExceptionを防ぐための対策

ループ処理中に要素を削除したい場合、以下のいずれかの方法を採用します。

1. Java 8以降ならremoveIfを使う(推奨)

もっとも安全でモダンな方法は、Collection#removeIfメソッドを使うことです。ループ自体を書かずに済むため、ConcurrentModificationExceptionのリスクがゼロになります。

List list = new ArrayList<>(List.of("A", "B", "C"));

// ループを使わず、条件に一致する要素を一括削除
list.removeIf(s -> s.equals("B"));

2. Iteratorを明示的に使う

細かい制御が必要な場合は、Iteratorを手動で取得して回します。

Iterator it = list.iterator();
while(it.hasNext()) {
    String s = it.next();
    if (s.equals("B")) {
        it.remove(); // 安全に削除可能
    }
}

これらの対策を押さえるだけで、反復処理の安全性が大きく向上します。詳細はAPIドキュメント(removeIf)でも確認できます。

まとめ|拡張for文を使いこなすための要点整理

本記事では、Java拡張for文の仕組み・書き方・使い分け・例外対策を体系的に解説しました。要点を整理すると以下のとおりです。

  • 拡張for文は配列やコレクションを簡潔に記述するための強力な構文。インデックス操作が不要で先頭から順番に処理するなら拡張for文が第一選択
  • Mapを処理する場合はentrySet()を使うのが定石。キーと値を効率よく取得できる
  • インデックスが必要・要素の入れ替えが必要・逆順処理などは通常for文を選ぶ
  • ループ中に要素を削除したい場合はremoveIf(Java 8以降推奨)またはIteratorを使用して例外を防ぐ

実務では「読み取るだけなら拡張for」「細かい制御や変更が必要なら通常forまたはIterator」という判断基準を持つことで、バグの少ない保守性の高いコードを書くことができます。まずは今日書いているコードで、拡張for文が適切かどうかを一度確認してみましょう。

つまずいたら、現役エンジニアと一緒に解決しよう

この記事で扱った【Java】は、独学だと細部で詰まりやすい分野です。さらに理解を深めたい方は、Zerocode Onlineで実務直結の学習を進めてみませんか。

現役エンジニア1on1

コード添削・詰まり解消を丁寧にサポート。

実務設計まで網羅

Java/SQL/Spring Bootを体系的に習得。

ポートフォリオ支援

現場で刺さる成果物づくりを伴走。

完全オンライン

時間と場所を選ばず学べます。

Join us! 未経験からエンジニアに挑戦できる環境で自分の可能性を信じてみよう 採用ページを見る→

記事監修

ドライブライン編集部

[ この記事をシェアする ]

記事一覧へ戻る