Javaのfor文を完全解説|基本構文・拡張for・Streamの実務的な使い分けガイド
Javaのfor文を完全解説|基本構文・拡張for・Streamの実務的な使い分けガイド
Javaのプログラミングにおいて、繰り返し処理(ループ)を適切に使い分けることは、コードの品質を左右する重要なスキルです。その中でも、for文は初心者から上級者までもっとも頻繁に使われる基本構文です。配列の操作・リストの処理・数値計算など、あらゆる場面で活躍します。
しかし、Javaには「基本for文」「拡張for文」「Stream API」という3つのループ処理が存在します。どれを使うべきか迷うことも少なくありません。そこで本記事では、それぞれの特徴を体系的に整理し、「いつ・どのforを選ぶべきか」を実務的な視点で解説します。拡張for文の詳細についてはJava拡張for文を完全解説|基本構文・配列/コレクション処理・例外回避まで総まとめもあわせてご覧ください。
for文の基本構造と実行の流れ
まず、もっとも基本的なfor文は「初期化→条件判定→本体処理→反復(更新)」という4つのステップで実行されます。この順序を正しく理解していないと、無限ループに陥ることがあります。また、意図した回数より1回多く(または少なく)実行してしまうトラブルにもつながります。
初期化式・条件式・反復式の役割
具体的には、以下のような構成で記述します。
for (int i = 0; i < 10; i++) {
System.out.println("現在のカウント: " + i);
}
このコードは4つの手順で処理が進みます。最初に初期化(Initialization)としてint i = 0が1回だけ実行されます。次に条件判定(Termination)としてi < 10を評価します。trueなら中身を実行し、falseならループが終了します。条件が通れば本体処理のブロック内を実行します。最後に反復(Increment)としてi++を実行して変数を更新し、再び条件判定に戻ります。
さらに、「条件判定」の書き方を間違えると、オフバイワンエラー(Off-by-one error)と呼ばれる「1回分ずれるバグ」が発生しやすくなります。例えば、配列の要素数に合わせてループする場合、i < array.lengthとすべきところをi <= array.lengthと書いてしまうケースが典型的です。この書き間違いにより、範囲外アクセスエラー(ArrayIndexOutOfBoundsException)が発生します。IPA「ソフトウェア開発分析データ集」でも、このような境界値バグは現場での頻出不具合として挙げられています。条件式を書く際は、「未満か、以下か」を毎回意識する習慣が重要です。
for文が選ばれる代表的な利用シーン
基本のfor文は記述が少し長くなりますが、柔軟性は最強です。Oracle公式チュートリアル(The for Statement)でも詳しく解説されているように、次のような場面では他の構文よりも優先して使われます。「何番目の要素か」をログに出したり計算に使うインデックス(添字)が必要な場面が代表例です。また、i += 2(2つ飛ばし)や逆順(i--)など特殊な増え方をする場合にも適しています。加えて、特定の条件でインデックスを巻き戻したりスキップしたりする複雑な制御が必要な場合も、基本for文の出番です。
実務で必須の制御構文と書き方の注意点
実務の現場では、単に回すだけでなく、特定の条件で処理を中断したりスキップしたりする制御が不可欠です。これらを正しく使い分けることで、コードの意図が明確になり保守性が上がります。
breakとcontinueの使い分け
ループの流れを変えるには、主にbreakとcontinueを使います。breakはループ処理を完全に終了し、for文の外へ抜ける命令です。探索処理で目的の要素が見つかった時点でループを終わらせたいときに活躍します。一方、continueは現在の周回(イテレーション)だけをスキップし、次の周回へ進む命令です。特定のエラーデータだけ除外したい場合に便利です。
for (int i = 0; i < 10; i++) {
if (i == 3) {
continue; // 3のときだけスキップ
}
if (i == 8) {
break; // 8になったらループ終了
}
System.out.println(i);
}
無限ループと省略形構文のリスク
また、for文の条件式はすべて省略することが可能です。
for (;;) {
// 無限ループ
}
ただし、意図しない無限ループはCPUリソースを食いつぶします。結果として、サーバーダウンの原因になることもあります。for(;;)を使う場合は、必ずブロック内に明確なbreak条件を記述してください。無限ループは使い所を限定し、「どの条件で抜けるか」を最初に設計しておくことが大切です。
ラベル付きbreakによる多重ループ脱出
さらに、ネスト(入れ子)されたfor文を一気に抜けたい場合は、ラベル付きbreakが有効です。
outerLoop: // ラベル定義
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i * j > 10) {
break outerLoop; // 外側のループまで一気に抜ける
}
}
}
一方で、多用すると処理の流れ(スパゲッティコード)が追いにくくなります。Java公式チュートリアルでも慎重な利用が推奨されています。したがって、ラベル付きbreakはネストが深くなる場面でのみ限定的に使いましょう。代わりにメソッド分割で解決できないかを先に検討することをおすすめします。
配列・コレクションを扱う拡張for文
Java 5からは、配列やコレクション(List・Setなど)をより簡潔に記述できる拡張for文が導入されました。インデックス管理や要素の取り出し記述が不要になるため、単純な全要素処理では基本for文よりもこちらを優先すべきです。
List names = Arrays.asList("Alice", "Bob", "Charlie");
// 拡張for文
for (String name : names) {
System.out.println(name);
}
このように「何を処理しているか」が一目でわかるため、可読性が大幅に向上します。コードレビューの場でも読み手の負担を減らせる、実務で重宝される書き方です。
使用時の注意点と制約
しかし、拡張for文にはいくつかの制約があります。まずインデックスが使えない点です。「現在の要素が何番目か」を知る方法がないため、必要な場合は別途カウンタ変数を外で用意するか、基本for文を使います。次に要素の削除ができない点も要注意です。ループ中にlist.remove(item)を行うとConcurrentModificationExceptionという例外が発生します。削除が必要な場合はIteratorまたはremoveIfメソッドを使用しましょう。また、Mapは直接回せないという制約もあります。Mapを回す場合はentrySet()やkeySet()を経由する必要があります。
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
Stream APIとの比較と使い分け基準
Java 8以降では、Stream APIを使った宣言的な書き方も主流になっています。for文が「処理の手順」を書くのに対し、Stream APIは「やりたいこと(結果)」を記述するスタイルです。データのフィルタリングやマッピングを行う場合、Stream APIの方が圧倒的に読みやすくなります。バグも少なくなる傾向があります。
// Stream APIの例
list.stream()
.filter(x -> x > 10)
.map(x -> x * 2)
.forEach(System.out::println);
では、実務ではどのように使い分ければよいのでしょうか。一般的な判断基準は次のとおりです。基本for文はインデックス操作が必要な場合や、break/continueによる複雑な制御が必要な場面で選びます。拡張for文は配列やリストの全要素をシンプルに読み取りたいときのデフォルト選択肢です。Stream APIはコレクションに対して「フィルタリング」「変換」「集計」などの複数操作を連鎖させたい場合に最適です。
したがって、「読み取るだけ→拡張for」「制御が複雑→基本for」「変換・集計→Stream」という判断軸を持つことで、コードの選択に迷わなくなります。さらに、チームで統一した基準を持つことが、コードレビューの効率化にもつながります。迷ったときは「このループで一番伝えたいことは何か」を起点に選びましょう。
まとめ
本記事では、Javaのfor文・拡張for文・Stream APIの仕組み・書き方・使い分けを体系的に解説しました。要点を整理すると以下のとおりです。
- 基本for文は「初期化→条件判定→本体→反復」の4ステップ。オフバイワンエラーに注意する
- break/continueは探索処理・データ除外でそれぞれ使い分ける。ラベル付きbreakは多用を避ける
- 拡張for文は全要素の読み取りに最適。ループ中の削除はIterator/removeIfを使う
- Stream APIはフィルタリング・変換・集計の連鎖操作に最も適している
- 「読み取るだけ→拡張for」「制御が複雑→基本for」「変換・集計→Stream」が実務の判断基準
まずは今日書いているコードで、3つの選択肢のどれが最適かを意識してみましょう。適切な構文の選択が、バグの少ない保守性の高いコードへの第一歩です。Javaを体系的に学びたい方は、現役エンジニアによる実務直結の学習環境を活用することで、理解がより深まります。
つまずいたら、現役エンジニアと一緒に解決しよう
この記事で扱った【Javaのfor文・繰り返し処理】は、独学だと細部で詰まりやすい分野です。さらに理解を深めたい方は、Zerocode Onlineで実務直結の学習を進めてみませんか。
コード添削・詰まり解消を丁寧にサポート。
Java/SQL/Spring Bootを体系的に習得。
現場で刺さる成果物づくりを伴走。
時間と場所を選ばず学べます。