タイトル: ネストされた繰り返し文でbreakする方法
SEOタイトル: Javaで多重ループを一気に抜ける方法|ラベル付きbreakの使い方
Javaでネストされた繰り返し文(多重ループ)から一気に外側まで抜けたい場合は、抜けたいループにラベルを付け、break ラベル名; と書く「ラベル付きbreak」を使うのが基本となる。通常のbreakは自分が属する最も内側のループ1つしか抜けられないため、外側のループまで一度に終了させたいときに有効な手法である。
| この記事の要点 |
|---|
|
通常のbreakでは内側のループ1つしか抜けられない
Javaのbreak文は、そのbreakを直接囲んでいる繰り返し文(for・while・do-while)またはswitchのうち、最も内側のもの1つを終了させる。そのため、ループがネストしている場合、内側のループの中でbreakしても外側のループは止まらず、外側の繰り返しは続行される。
|
for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (j == 1) { break; // 内側のforだけを抜ける } } // ← ここに戻り、外側のforは続く } |
上の例では、内側のbreakによって内側のforは終了するが、外側のforはそのまま次のiへ進む。外側まで一気に抜けたい場合には、以下のいずれかの方法を用いる。
方法①:ラベル付きbreak
Javaでは、繰り返し文の直前にラベル名: という形でラベルを付けられる。break ラベル名; と書くと、そのラベルが付いた繰り返し文を終了できるため、外側のループに抜けたい先のラベルを付けておけば、内側からでも一気に外側まで抜けられる。
|
outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (i * j > 2) { break outer; // outer ラベルのforごと抜ける } System.out.println(i + "," + j); } } System.out.println("ループ終了"); |
ラベル名は識別子であれば自由に付けられる(例ではouter)。break outer; が実行されると、内側・外側のforが両方とも終了し、外側ループの直後(System.out.println("ループ終了");)へ処理が移る。
| ラベルに関する注意点 |
|---|
|
方法②:フラグ変数で制御する
boolean型のフラグ変数を用意し、抜けたい条件が成立したらフラグを立て、各ループの継続条件やbreakでそれを参照する方法である。ラベルを使わずに記述できるが、フラグの判定を各ループに書く必要があり、コードがやや冗長になりやすい。
|
boolean found = false; for (int i = 0; i < 3 && !found; i++) { for (int j = 0; j < 3; j++) { if (i * j > 2) { found = true; break; // 内側を抜ける } } } |
この例では、内側でbreakして内側ループを抜けたあと、外側ループの継続条件!foundがfalseになることで外側ループも終了する。フラグを参照するタイミングによっては、フラグを立てた後に余分な処理が1回走ることがあるため、判定位置に注意が必要である。
方法③:ループをメソッドに切り出してreturnする
ネストしたループ全体を1つのメソッドに切り出し、抜けたい時点でreturnすると、メソッドごと処理を抜けられる。ループの目的が「条件に合う値を探して返す」といった場合には、見つけた値をそのままreturnでき、意図が明確になりやすい。
|
int[] search(int[][] data, int target) { for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { if (data[i][j] == target) { return new int[]{i, j}; // メソッドごと抜ける } } } return null; // 見つからなかった場合 } |
3つの手法の比較
どの手法を選ぶかは、可読性や処理の目的によって変わる。代表的な特徴を整理する。
| 手法 | 書き方 | 可読性 | 向いている場面 |
|---|---|---|---|
| ラベル付きbreak | outer: for(...){ ... break outer; } |
ループ構造のまま簡潔に書ける | 多重ループから一気に抜けたいとき |
| フラグ変数 | boolean flag; ... 条件 && !flag |
判定が各ループに分散しやすい | ラベルを避けたい、継続条件に組み込みたいとき |
| メソッド + return | ループをメソッド化しreturn |
目的が明確なら高い | 探索結果を返すなど処理単位が分けられるとき |
ラベル付きcontinueも使える
ラベルはbreakだけでなくcontinueでも使える。continue ラベル名; と書くと、指定したラベルの繰り返し文の次の繰り返しへ処理を移せる。外側ループの次の周回へスキップしたいときに役立つ。
|
outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (j == 1) { continue outer; // 外側forの次の i へ } System.out.println(i + "," + j); } } |
この例では、jが1になった時点で内側ループを抜け、外側ループの次のiへ進む。出力されるのは各iについてj == 0のときのみとなる。
落とし穴・注意点
| つまずきやすいポイント |
|---|
|
具体的なコード例(まとめ)
2次元配列から特定の値を探し、見つかった時点で多重ループを抜ける例を、ラベル付きbreakで示す。
|
int[][] data = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; int target = 5; int foundI = -1, foundJ = -1; search: for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { if (data[i][j] == target) { foundI = i; foundJ = j; break search; } } } System.out.println("見つかった位置: " + foundI + "," + foundJ); |
この例では、target(5)が見つかった時点でbreak search; により内側・外側のforが同時に終了し、見つけた位置がそのまま保持される。実行すると 見つかった位置: 1,1 が出力される。
FAQ(よくある質問)
Q1. ラベル名に決まった書き方はあるか?
ラベル名は変数名などと同じく識別子の規則に従えば自由に付けられ、outer や loop のように内容が分かる名前にすると読みやすい。なお、ラベル名は同じスコープ内で重複しないように付ける必要がある。
Q2. 3重以上のループでも一気に抜けられるか?
抜けたい一番外側のループにラベルを付けておけば、break ラベル名; によって内側の何重のループからでもそのラベルのループまで一気に抜けられる。ネストが深くなるほど、ラベル付きbreakやメソッド分割の利点が大きくなる。
Q3. breakとcontinueでラベルの効果はどう違うか?
break ラベル名; は指定したラベルの繰り返し文を終了するのに対し、continue ラベル名; は指定したラベルの繰り返し文の次の繰り返しへ進む。ループ自体を止めたいのか、その周回だけ飛ばして続けたいのかで使い分ける。