Javaプログラミングにおいて、繰り返し処理はプログラムの論理構造を支える非常に重要な要素です。
特定の条件が満たされている間、同じ処理を何度も実行させることで、膨大なデータの処理やユーザー入力を待ち受ける待機状態などを実現できます。
Javaにはいくつかの繰り返し構文が存在しますが、その中でも 「while文」は条件式のみに依存して動作を制御する最もシンプルで強力な制御構造 の一つです。
本記事では、while文の基本的な書き方から、実務で役立つ応用テクニック、そして初心者もベテランも陥りやすい無限ループの防ぎ方までを詳しく解説します。
Javaのwhile文とは?基本構造と動作原理
Javaの while 文は、指定した条件式が true (真) である間、ブロック内の処理を繰り返し実行する制御文です。
指定した回数だけ繰り返す for 文とは異なり、「いつ終わるか正確には分からないが、特定の条件を満たすまでは続けたい」 というケースに非常に適しています。
基本的な構文は以下の通りです。
while (条件式) {
// 条件式がtrueの間、繰り返し実行される処理
}
この構文において、まず最初に 条件式 が評価されます。
その結果が true であれば中括弧 {} 内の処理が実行され、実行が終わると再び 条件式 の評価に戻ります。
もし最初の評価時点で false であった場合、中身の処理は 一度も実行されずに終了 します。
while文の動作フロー
while文の動きを整理すると、以下の3つのステップの繰り返しとなります。
- 条件式の評価:括弧内の式が
trueかfalseかを判定する。 - 処理の実行:判定が
trueの場合、ブロック内のコードを実行する。 - ループの再開:ブロックの最後まで到達したら、再び手順1に戻る。
この単純な仕組みこそが、柔軟なプログラム制御を可能にします。
基本的なwhile文の使用例
まずは、最もシンプルな数値のカウントアップを例に、具体的なコードを見ていきましょう。
public class WhileBasicExample {
public static void main(String[] args) {
int count = 1;
// countが5以下の間、処理を繰り返す
while (count <= 5) {
System.out.println("現在のカウントは: " + count);
// カウントを1増やす(これを忘れると無限ループになる)
count++;
}
System.out.println("ループが終了しました。");
}
}
現在のカウントは: 1
現在のカウントは: 2
現在のカウントは: 3
現在のカウントは: 4
現在のカウントは: 5
ループが終了しました。
このプログラムでは、変数 count が5以下であるかどうかを毎回チェックしています。
ループ内で count++ を行うことで、毎周期ごとに値が増加し、最終的に条件式が false となってループを抜けます。
while文とfor文の使い分け
Javaにはもう一つの主要な繰り返し文である for 文があります。
これらは機能的に書き換えることが可能ですが、ソースコードの可読性を高めるためには、用途に応じた適切な使い分けが必要です。
for文が適しているケース
「繰り返す回数があらかじめ決まっている場合」 は、for 文を使用するのが一般的です。
配列の全要素を走査したり、100回処理を行ったりする場合、初期化・条件式・更新式を一行にまとめられる for 文の方がコードがスッキリします。
while文が適しているケース
一方、「特定の状態になるまで続けたい場合」 や 「処理の結果によって終了タイミングが決まる場合」 は while 文が最適です。
例えば、ファイルの中身を最後まで読み取る、ユーザーが「exit」と入力するまで待機する、あるいは特定の計算結果が目標値に達するまで繰り返すといった処理が該当します。
以下の表に、主な違いをまとめました。
| 特徴 | while文 | for文 |
|---|---|---|
| 主な用途 | 終了条件が動的な場合 | 実行回数が固定の場合 |
| 初期化式 | ループの外で行う | 構文内に含まれる |
| 更新式 | ブロック内で記述が必要 | 構文内に含まれる |
| 可読性 | 状態監視のロジックに強い | カウント制御のロジックに強い |
do-while文:必ず一度は実行したい場合
while文には、派生形として do-while 文が存在します。
通常のwhile文が「前判定」であるのに対し、do-while文は 「後判定」 という特徴を持っています。
do {
// 最初に一度実行され、その後条件がtrueの間繰り返される
} while (条件式);
通常のwhile文では、条件が最初から false の場合に一度も実行されませんが、do-while 文は 条件の成否に関わらず必ず最低1回はブロック内の処理が実行 されます。
これは、ユーザーに対してメニューを表示し、その後の入力内容によって継続を判断するようなUIプログラムでよく利用されます。
do-while文の具体例
import java.util.Scanner;
public class DoWhileExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int inputNumber;
do {
System.out.print("10以上の数字を入力してください(10未満で終了): ");
inputNumber = scanner.nextInt();
System.out.println("入力された値: " + inputNumber);
} while (inputNumber >= 10);
System.out.println("終了します。");
scanner.close();
}
}
この例では、ユーザーが最初に入力する値を確認する前に、入力を促すメッセージを表示する必要があります。
このような「まず動かして、その結果で継続を決める」シーンに最適です。
ループを制御する:breakとcontinue
while文のループの流れをより細かく制御するために、break 文と continue 文が用意されています。
これらを使いこなすことで、複雑な条件分岐を持つループを簡潔に記述できます。
break文による途中離脱
break 文は、ループを強制的に終了させ、ブロックから抜け出す ために使用します。
例えば、探索処理で見つけたいデータが見つかった瞬間にループを止めたい場合に有効です。
int i = 0;
while (true) { // 無限ループの形
if (i == 10) {
break; // iが10になった瞬間にループを抜ける
}
i++;
}
continue文による処理のスキップ
continue 文は、「その回の残りの処理をスキップして、次の条件評価(または更新式)へ移動する」 ために使用します。
特定の条件下でのみ処理を行いたくない場合に便利です。
int i = 0;
while (i < 10) {
i++;
if (i % 2 == 0) {
continue; // 偶数の場合は、以下のprint処理を飛ばして次のループへ
}
System.out.println("奇数です: " + i);
}
奇数です: 1
奇数です: 3
奇数です: 5
奇数です: 7
奇数です: 9
無限ループの原因と防ぎ方
while文を利用する上で最も警戒すべきなのが 無限ループ です。
条件式がいつまでも false にならない場合、プログラムは永遠に停止せず、CPUリソースを過剰に消費し続け、最悪の場合はシステムダウンを引き起こします。
無限ループが発生する主なパターン
- 更新式の記述忘れ
カウンタ変数を使っている場合に、ループ内でその変数を加算・減算し忘れると、条件式が常に固定され、無限ループに陥ります。
- 条件式の論理ミス
例えば、
while (i >= 0)という条件で、ループ内でiを増やし続けてしまうと、数値がオーバーフローしない限り条件は常に真となります。- 意図的な無限ループからの脱出失敗
while (true)を使用した際、特定の条件でbreakするロジックに不備があると、出口のないループになります。
無限ループを防ぐためのベストプラクティス
無限ループを未然に防ぐためには、以下の点に注意してコーディングを行いましょう。
- ループ変数の更新を確認する
ループの最後に、条件式に関わる変数が正しく変更されているかを必ずチェックします。
- 複合条件の単純化
条件式が複雑すぎると、予期せぬケースで
trueが返り続けることがあります。条件を整理するか、途中でログを出力して動作を確認してください。
- セーフティネットの実装
開発中は、万が一のために最大実行回数をカウントし、それを超えたらエラーとして中断する処理を一時的に入れるのも有効な手段です。
int safetyCounter = 0;
while (complexCondition) {
// 処理
safetyCounter++;
if (safetyCounter > 1000000) { // 100万回を超えたら異常事態
System.err.println("無限ループの可能性があるため中断します");
break;
}
}
実践的なwhile文の活用シーン
while文は、単純な繰り返し以上の場面で真価を発揮します。
ここでは、Javaの実務開発でよく見られる2つの具体的な活用シナリオを紹介します。
1. ファイルの読み込み
Javaでテキストファイルを一行ずつ読み込む際、BufferedReader の readLine() メソッドが null を返すまで繰り返すというパターンが非常に一般的です。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileReadExample {
public static void main(String[] args) {
// try-with-resourcesを使用して自動的にクローズ
try (BufferedReader reader = new BufferedReader(new FileReader("sample.txt"))) {
String line;
// readLine()の結果がnullでない間、繰り返し読み込む
while ((line = reader.readLine()) != null) {
System.out.println("読み込んだ行: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
この構造は、データの総量が事前に分からないストリーム処理において、while文が最も得意とする分野です。
2. イテレータを使用したコレクション操作
List や Set などのコレクションを操作する際、Iterator を使用して要素を取り出すときにもwhile文が使われます。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Ruby");
Iterator<String> it = list.iterator();
// 次の要素が存在する間、ループを継続
while (it.hasNext()) {
String lang = it.next();
System.out.println("言語名: " + lang);
}
}
}
現代のJavaでは拡張for文やStream APIを使う機会も増えていますが、「ループの途中で要素を安全に削除したい」 といった場合には、このIteratorとwhile文の組み合わせが必要不可欠となります。
ネストしたwhile文とラベル付きbreak
while文の中にさらに別のwhile文を記述することを「ネスト(入れ子)」と呼びます。
多次元のデータを扱う場合などに使用されますが、深いネストは可読性を著しく低下させるため注意が必要です。
また、ネストした深い階層から一気に外側のループまで抜け出したい場合には、「ラベル付きbreak」 という機能が利用できます。
public class LabeledBreakExample {
public static void main(String[] args) {
outerLoop: // ラベルの定義
while (true) {
System.out.println("外側ループ開始");
while (true) {
System.out.println("内側ループ");
break outerLoop; // 外側のループごと抜ける
}
}
System.out.println("すべてのループを終了しました。");
}
}
外側ループ開始
内側ループ
すべてのループを終了しました。
通常の break では直近のループしか抜けられませんが、ラベルを使用することで特定のループを指定して制御することが可能です。
ただし、多用するとプログラムの構造が追いづらくなる(スパゲッティコードの原因になる)ため、利用は最小限に留めるべきです。
while文のデバッグとトラブルシューティング
プログラムが期待通りに動作しない場合、while文の周辺に原因があることが少なくありません。
特に以下のような現象が発生した際は、while文の挙動を疑ってみてください。
- 処理が全く実行されない
条件式が最初から
falseになっていませんか?変数の初期化タイミングや、条件式の比較演算子(>と>=の違いなど)を再確認してください。- 処理が1回多い、または1回少ない
これは「オフバイワン・エラー(Off-by-one error)」と呼ばれる典型的なバグです。
境界値の判定ミスが原因であることが多いため、手動で1ステップずつ変数の動きを追ってみる(机上デバッグ)のが有効です。
- メモリ使用量が急激に増える
無限ループ内で新しいオブジェクトを生成し続けている可能性があります。
デバッグを容易にするためには、ループの開始直前やブロックの先頭で System.out.println() を使い、現在の条件変数の値をコンソールに出力して可視化するのが最も確実な方法です。
while文のパフォーマンスに関する考察
while文自体の実行速度は非常に高速ですが、ループ内での処理内容によっては全体のパフォーマンスに大きな影響を与えます。
ループ不変式の追い出し
ループの中で毎回計算する必要がない式(ループ不変式)を、ループの外に移動させることで無駄な計算を省くことができます。
// 非効率な例
while (i < list.size()) {
double result = Math.sqrt(100.0); // 毎回同じ計算をしている
// ...
}
// 効率的な例
double result = Math.sqrt(100.0);
while (i < list.size()) {
// ...
}
適切な待機処理(スリープ)
無限ループで特定のイベントを待つようなプログラム(ポーリング処理)の場合、何も対策をしないとCPU使用率が100%に張り付いてしまいます。
そのような場合は、Thread.sleep() を挿入して、適切な間隔で処理を休ませることが重要です。
while (true) {
if (checkStatus()) {
break;
}
try {
Thread.sleep(100); // 100ミリ秒待機してCPU負荷を抑える
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
まとめ
Javaの while 文は、柔軟な繰り返し処理を実現するための根幹となるツールです。
本記事では、基本構文から do-while 文との違い、break / continue による制御、そして注意すべき無限ループの対策まで幅広く解説しました。
while文をマスターする上での重要ポイントは以下の3点です。
- 条件がいつ false になるかを常に意識する
ループ変数の更新を忘れず、終了条件を明確に設計しましょう。
- 用途に合わせて for 文と使い分ける
回数指定なら
for、状態監視ならwhileを選ぶのが保守性の高いコードへの近道です。- 複雑な制御には慎重になる
ネストやラベル付き
breakは強力ですが、多用を避けてシンプルで読みやすいコードを心がけましょう。
繰り返し処理は、大量のデータを扱う現代のソフトウェア開発において欠かせない技術です。
while文の挙動を深く理解し、正しく使いこなすことで、より堅牢で効率的なJavaプログラムを構築できるようになります。
この記事で学んだ内容を、ぜひ日々のコーディングに役立ててください。






