タイトル: continue文 (繰り返し制御文)
SEOタイトル: Java continue 文 完全ガイド(基本/label付き/break との違い/Stream で代替)
| この記事の要点 |
|
continue 文の基本
continue; は現在の反復(イテレーション)を打ち切り、ループ条件の判定に戻ります。残りの本体は実行されません。
// 偶数だけ出力
for (int i = 1; i <= 10; i++) {
if (i % 2 != 0) {
continue; // 奇数はスキップして次の i へ
}
System.out.println(i);
}
// 出力: 2 4 6 8 10
// while でも同様
int i = 0;
while (i < 10) {
i++;
if (i % 2 != 0) continue;
System.out.println(i);
}
break との違い
| 文 | 動作 | 典型用途 |
|---|---|---|
break; | ループ全体を抜ける | 条件を満たす要素を見つけたら終了 |
continue; | 現在の反復だけスキップ、ループは続行 | 不要な要素を飛ばして処理を続ける |
return; | メソッド自体を終了 | 結果が出たら呼び出し元へ戻す |
// 検索: 見つけたら break
int target = -1;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == 42) {
target = i;
break; // ★ ループ全体終了
}
}
// フィルタ: 不正値だけ飛ばす
for (String line : lines) {
if (line.isBlank() || line.startsWith("#")) {
continue; // ★ この行だけスキップ
}
process(line);
}
label 付き continue(外側ループへスキップ)
多重ループの中で、外側のループに対して continue / break したい場合に使います:
// 二次元配列で、行に 0 が含まれる場合その行をスキップ
outer:
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == 0) {
continue outer; // ★ 外側の for に対して次の i へ
}
}
System.out.println("行 " + i + " は 0 を含まない");
}
// 比較: label なしだと内側だけスキップ
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == 0) {
continue; // 内側の for の次の j へ
}
// ...
}
}
ガード節としての continue
ループ本体の冒頭で処理対象外を早期に弾くと、本体のネストが浅くなり読みやすくなります:
// ❌ ネストが深い
for (User user : users) {
if (user != null) {
if (user.isActive()) {
if (user.getEmail() != null) {
sendNewsletter(user);
}
}
}
}
// ✅ ガード節 + continue で平坦化
for (User user : users) {
if (user == null) continue;
if (!user.isActive()) continue;
if (user.getEmail() == null) continue;
sendNewsletter(user);
}
無限ループに注意
while ループでカウンタを更新する前に continue すると無限ループになります:
// ❌ 無限ループ! i++ に到達しない
int i = 0;
while (i < 10) {
if (i % 2 != 0) continue; // i は 1 のまま
System.out.println(i);
i++;
}
// ✅ カウンタ更新を先に
int i = 0;
while (i < 10) {
i++;
if (i % 2 != 0) continue;
System.out.println(i);
}
// ✅ for で書けばこの問題は起きない(更新式が必ず実行される)
for (int i = 0; i < 10; i++) {
if (i % 2 != 0) continue;
System.out.println(i);
}
Stream API での代替
Java 8+ ではStream の filter()で同等の処理を宣言的に書けます。可読性が高く、並列化も容易:
// for + continue の書き方
List<User> activeUsers = new ArrayList<>();
for (User user : users) {
if (user == null) continue;
if (!user.isActive()) continue;
activeUsers.add(user);
}
// Stream で書き換え
List<User> activeUsers = users.stream()
.filter(Objects::nonNull)
.filter(User::isActive)
.collect(Collectors.toList());
// 副作用を伴うループ
users.stream()
.filter(Objects::nonNull)
.filter(User::isActive)
.filter(u -> u.getEmail() != null)
.forEach(this::sendNewsletter);
continue を使うべき場面 / 避けるべき場面
| 場面 | 判断 |
|---|---|
| ガード節として先頭で異常系を弾く | 使う(推奨) |
| 反復処理の途中で集約から除外 | 使う |
| 複雑な条件を組み合わせて多重 continue | 避ける(Streamに書き換え検討) |
| label 付き continue を多用 | 避ける(メソッド抽出推奨) |
パフォーマンスの観点
continue は単純なジャンプ命令で、条件分岐と同じコスト。性能上のペナルティはありません。ただし:
- continue が多すぎる = フィルタリング処理が冗長 → Stream / 事前フィルタ検討
- label 付き continue はJIT 最適化が落ちることがある。クリティカルパスなら避ける
- 大量データなら parallelStream でデータ分割並列化
デバッグの Tip
// continue 位置にブレークポイントを置くか、ログを入れて挙動確認
int skipped = 0;
for (User user : users) {
if (user == null) { skipped++; continue; }
if (!user.isActive()) { skipped++; continue; }
process(user);
}
log.info("Processed {} users, skipped {}", users.size() - skipped, skipped);
FAQ
Q: continue と break どちらを使うか迷う
A: 「ループは続けたい」なら continue、「もう終わり」なら break。前者はフィルタ、後者は探索のイメージ。
Q: do-while でも continue は使える?
A: 使えます。continue するとループ末尾の条件式に飛びます。
Q: Lambda 内では continue 使えない?
A: 使えません。Lambda は別のスコープなので、return; を使うと「その要素の処理だけ終了」=継続として動きます(forEach での return; = continue 相当)。