無限ループの全体像
無限ループは「終了条件が永遠に満たされず、ループが止まらない状態」です。Java では while (true) や for(;;) のように意図的に書くこともできますが、初心者が陥りやすいのは「終了条件の設計ミス」「状態更新の漏れ」「比較の取り違え」です。止まらないだけでなく、CPU を占有してアプリ全体を固めることもあるため、原因と対策を体系的に押さえることが大切です。
典型的な発生原因
終了条件が間違っている、または到達不能になっていると止まりません。更新が分岐で飛ぶ、continue で更新箇所をスキップする、浮動小数点の誤差で「いつまでも条件が真」のままになる、といった落とし穴が代表例です。入力待ちやブロッキング I/O で「止まらないように見える」ケースもあり、無限ループと見分ける必要があります。
よくあるコード例と直し方
状態更新の漏れ
int i = 0;
while (i < 3) {
System.out.println(i);
// i++ がない → 永遠に i は 0 のまま
}
Java更新は必ず通る構造にします。本体の最後に置くか、分岐があるならどの分岐でも更新が走るようにします。
int i = 0;
while (i < 3) {
System.out.println(i);
i++;
}
Java終了条件の取り違え(比較の向き)
int remain = 3;
while (remain >= 0) { // 0 でも入るので、更新次第では無限ループに
System.out.println(remain);
remain--; // -1, -2... と続いて抜けない
}
Java「減らすなら 0 より大きい」「増やすなら未満」を基本形にそろえると安定します。
int remain = 3;
while (remain > 0) {
System.out.println(remain);
remain--;
}
Javacontinue で更新が飛ぶ
int i = 0;
while (i < 5) {
if (i == 2) {
continue; // ここで i++ を飛ばす → i==2 のまま無限ループ
}
System.out.println(i);
i++;
}
Javacontinue 前に更新を入れるか、更新を先頭へ寄せます。
int i = 0;
while (i < 5) {
if (i == 2) { i++; continue; }
System.out.println(i);
i++;
}
Java浮動小数点の比較
double x = 0.0;
while (x != 1.0) { // 誤差で 1.0 に「ちょうど」ならない可能性
x += 0.1;
}
Java誤差を考慮した条件にします。イプシロンを使う、または回数で制御します。
double x = 0.0;
while (Math.abs(x - 1.0) > 1e-9) {
x += 0.1;
if (x > 2.0) break; // セーフティ
}
Java入力待ちで進まない(無限ループと誤解)
java.io.BufferedReader br = ...;
String line;
while ((line = br.readLine()) != null) {
// 入力が来ないとここでブロックし続ける(停止ではなく待機)
}
Java「待機」と「無限」を区別します。タイムアウトや番兵値(空行など)で終了条件を明示すると把握しやすくなります。
安全弁の設計(深掘り)
試行回数の上限
上限で必ず止める発想です。「成功するまで、ただし N 回まで」にすると、失敗時でも脱出できます。
int attempts = 0, max = 3;
boolean success = false;
while (!success && attempts < max) {
success = tryOnce();
attempts++;
}
Java時間上限(締切)
「一定時間が過ぎたら終了」。時間は「最初に測る」「各周回でチェック」のセットで。
long deadline = System.nanoTime() + 1_000_000_000L; // 1秒
while (workRemaining()) {
step();
if (System.nanoTime() > deadline) break;
}
Java番兵値・hasNext の活用
入力が尽きたら終了、特定の値で終了、といった「出口」を条件に直書きすると安全です。
java.util.Scanner sc = new java.util.Scanner("10 20 30");
int sum = 0;
while (sc.hasNextInt()) {
sum += sc.nextInt();
}
Java進捗チェックとスピン抑制
「何も進んでいない」周回が続くなら抜けるか休む。CPU 暴走を防ぎます。
int prev = state();
while (true) {
doWork();
int cur = state();
if (cur == prev) { Thread.sleep(1); } // 進捗なしなら休む
prev = cur;
}
Java早期終了の道具と使い分け
break・return・ラベル付き break
- ループだけ終わらせたいなら
break。 - メソッドごと終わらせたいなら
return。 - 多重ループをまとめて抜けたいならラベル付き
break。
outer:
for (int r = 0; r < R; r++) {
for (int c = 0; c < C; c++) {
if (found(r, c)) break outer; // 外側まで一気に脱出
}
}
Java「どこまで止めたいか」を軸に選ぶと、フローが明快になります。
調査・デバッグのコツ
ループの上限やタイムアウトを仮で入れ、進捗ログ(周回番号、主要変数)を出すと原因が見えます。境界テスト(空入力、1件、大量、先頭・末尾)で終了条件を揺さぶるのが有効です。インクリメント/デクリメントを式に混ぜず、更新位置を一箇所に固定すると追いやすくなります。
実用例で総復習
線形探索で「見つかったら終わり」
public class FindFirst {
public static void main(String[] args) {
String[] names = {"abe", "sato", "suzuki", "sakai"};
String found = null;
for (String n : names) {
if (n.startsWith("sa")) { found = n; break; }
}
System.out.println(found); // sato
}
}
Javaログ読みで「空行まで」
import java.util.Scanner;
public class UntilBlank {
public static void main(String[] args) {
Scanner sc = new Scanner("A\nB\n\nC\n");
String line;
while ((line = sc.nextLine()) != null && !line.isBlank()) {
System.out.println("read: " + line);
}
System.out.println("stop at blank");
}
}
Javaリトライ+上限の安全弁
public class Retry {
public static void main(String[] args) {
int attempts = 0, max = 3;
boolean ok;
do {
ok = tryOnce();
attempts++;
} while (!ok && attempts < max);
System.out.println(ok ? "OK" : "NG");
}
static boolean tryOnce() { return Math.random() < 0.4; }
}
Java仕上げのアドバイス(重要部分のまとめ)
終了条件は「比較軸」と「更新軸」をペアで設計し、更新が必ず通る構造にします。continue を使う分岐でも更新一貫性を保ち、浮動小数点はイプシロンか回数制御で安全に。実務では「回数上限」「時間上限」「番兵値」を積み、必要なら break/return/ラベル付き break で早期終了を明確化します。進捗ログと境界テストで原因を特定し、CPU スピンは休ませる。この流儀を守れば、無限ループは怖くありません。
