Javaのfor文を完全解説|基本構文・拡張for・Streamの実務的な使い分けガイド
CONTENTS
Javaのプログラミングにおいて、繰り返し処理(ループ)を適切に使い分けることは、コードの品質を左右する重要なスキルです。その中でも、for文は初心者から上級者までもっとも頻繁に使われる基本構文であり、配列の操作、リストの処理、数値計算など、あらゆる場面で活躍します。
しかし、Javaには大きく分けて「基本for文」「拡張for文」「Stream API」という3つのループ処理が存在し、どれを使うべきか迷うことも少なくありません。そこで本記事では、それぞれの特徴を体系的に整理し、「いつ・どのforを選ぶべきか」を実務的な視点で解説します。
for文の基本構造と実行の流れ
まず、もっとも基本的なfor文は、「初期化→条件判定→本体処理→反復(更新)」という4つのステップで実行されます。この順序を正しく理解していないと、無限ループに陥ったり、意図した回数より1回多く(または少なく)実行してしまうトラブルにつながります。
初期化式・条件式・反復式の役割
具体的には、以下のような構成で記述します。
for (int i = 0; i < 10; i++) {
System.out.println("現在のカウント: " + i);
}
このコードは、以下の手順で処理が進みます。
- 初期化(Initialization):
int i = 0が最初に1回だけ実行されます。 - 条件判定(Termination):
i < 10を評価します。これがtrueなら中身を実行し、falseならループを終了します。 - 本体処理: ブロック
{ ... }の中の処理を実行します。 - 反復(Increment):
i++を実行し、変数を更新します。その後、2.に戻ります。
しかし、この「条件判定」の書き方を間違えると、オフバイワンエラー(Off-by-one error)と呼ばれる「1回分ずれるバグ」が発生しやすくなります。たとえば、配列の要素数に合わせてループする場合、i < array.lengthとすべきところをi <= array.lengthと書いてしまい、範囲外アクセスエラー(ArrayIndexOutOfBoundsException)になるケースが典型的です。
for文が選ばれる代表的な利用シーン
基本のfor文は、記述が少し長くなりますが、柔軟性は最強です。次のような場面では、他の構文よりも優先して使われます。
- インデックス(添字)が必要な場合: 「何番目の要素か」をログに出したり、計算に使ったりするとき
- 特殊な増え方をする場合:
i++ではなくi += 2(2つ飛ばし)や、逆順(i--)で回したいとき - 条件付きでループを操作したい場合: 特定の条件でインデックスを巻き戻したり、スキップしたりするとき
正確な仕様については、Oracle公式チュートリアル(The for Statement)も併せて確認しておくと安心です。
実務で必須の制御構文と書き方の注意点
実務の現場では、単に回すだけでなく、特定の条件で処理を中断したり、スキップしたりする制御が不可欠です。
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公式チュートリアルでも慎重な利用が推奨されています。
配列・コレクションを扱う拡張for文(Enhanced for)
Java 5からは、配列やコレクション(List、Setなど)をより簡潔に記述できる拡張for文が導入されました。
拡張for文のメリットと書き方
拡張for文を使うと、インデックス管理や要素の取り出し記述が不要になります。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 拡張for文
for (String name : names) {
System.out.println(name);
}
このように、「何を処理しているか」が一目でわかるため、可読性が大幅に向上します。単純に全要素を処理したい場合は、基本for文よりもこちらを優先すべきです。
使用時の注意点と制約
一方で、拡張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の違い
for文は「処理の手順」を書くのに対し、Stream APIは「やりたいこと(結果)」を記述するスタイルです。
// Stream APIの例
list.stream()
.filter(x -> x > 10)
.map(x -> x * 2)
.forEach(System.out::println);
データのフィルタリング(絞り込み)やマッピング(加工)を行う場合、Stream APIの方が圧倒的に読みやすく、バグも少なくなります。
パフォーマンスと可読性の選択基準
では、実務ではどのように使い分ければよいのでしょうか。一般的な基準は以下の通りです。
- 基本for文: インデックス操作が必要、パフォーマンスが最重要(ごくわずかですがforの方が高速)、または複雑な条件分岐でbreak/continueが必要な場合
- 拡張for文: 単純に配列やリストの全要素を読み取りたい場合。もっとも推奨されるデフォルトの選択肢です
- Stream API: コレクションに対して「フィルタリング」「変換」「集計」などの複雑な操作を行う場合
まとめ:for文を正しく使い分けてコード品質を高めよう
Javaのfor文は単純に見えて奥が深く、用途に応じて適切な構文を選ぶことで、コードの可読性・保守性・性能が大きく向上します。
- カウンタ操作や複雑な制御が必要 → 基本for文
- 単純なデータの走査・読み取り → 拡張for文
- データの加工・フィルタリング・集計 → Stream API
このように、それぞれの特性を理解し、目的に応じて最適な構文を選ぶことが、バグの少ない安定したコードを書くための第一歩です。
つまずいたら、現役エンジニアと一緒に解決しよう
この記事で扱った【Javaのfor文・繰り返し処理】は、独学だと細部で詰まりやすい分野です。さらに理解を深めたい方は、Zerocode Onlineで実務直結の学習を進めてみませんか。
コード添削・詰まり解消を丁寧にサポート。
Java/SQL/Spring Bootを体系的に習得。
現場で刺さる成果物づくりを伴走。
時間と場所を選ばず学べます。