Java 入門

Java break と continue 文

Java において、breakcontinue ステートメントはループの実行を制御するための強力なパワーを提供します。これらを使用することで、forwhile、および do-while ループの標準的な実行フローを変更し、特定のコンディション(条件)に基づいてイテレーション(繰り返し)をスキップしたり、ループ全体を完全に終了させたりすることが可能になります。

1. break 文

break 文は、ループを途中で終了させるために使用されます。ループ内部で break 文が実行されると、そのループの処理は即座に停止し、プログラムのコントロール(制御)はループ直後の次のコード行に移動します。

1.1 基本的な使い方

break の最も一般的なユースケースは、特定の条件が満たされたときに即座にループを脱出することです。以下のサンプルを見てみましょう。

public class BreakExample {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                break; // i が 5 のとき、ループを即座に終了する
            }
            System.out.println("i = " + i);
        }
        System.out.println("ループが終了しました。");
    }
}

コードの解説:

  1. for ループを初期化し、1 から 10 までイテレーションするよう設定します。
  2. ループ内で if 文を使い、i が 5 と等しいかどうかをチェックします。
  3. i が 5 になった瞬間、break 文がトリガーされます。
  4. break 文はこのループを強制終了させます。
  5. プログラムはループ外の次の行を実行し、「ループが終了しました。」とプリントします。

プログラムの出力:

i = 1
i = 2
i = 3
i = 4
ループが終了しました。

i が 5 に達した時点でループが停止し、その後の 5 から 10 までのイテレーションがすべてスキップされている点に注目してください。

1.2 ネストされたループにおける break (ラベル付き break)

ネストされたループ(ループの中に別のループが入っている構造)を扱う場合、通常の break 文はそれを囲んでいる最も内側のループのみを終了させます。内側のループから外側のループまで一気に「突き破って」脱出したい場合は、ラベル付き (labeled) break 文を使用します。

public class NestedBreakExample {
    public static void main(String[] args) {
        outerLoop: // 外側のループに 'outerLoop' という名前のラベルを定義
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= 3; j++) {
                if (i == 2 && j == 2) {
                    break outerLoop; // 'outerLoop' という名前のラベルが付いた外側のループを脱出
                }
                System.out.println("i = " + i + ", j = " + j);
            }
        }
        System.out.println("ネストされたループが終了しました。");
    }
}

コードの解説:

  1. 2 層にネストされた for ループがあります。外側と内側はどちらも 1 から 3 までイテレーションします。
  2. 外側のループに outerLoop というラベルを付けます。
  3. 内側のループで、i が 2 かつ j も 2 であるかをチェックします。
  4. 条件が満たされると、break outerLoop; が実行されます。この命令は、outerLoop ラベルが貼られたループを直接中断して脱出するようプログラムに伝えます。
  5. その結果、i=2, j=2 のときに内外両方のループが同時に終了し、プログラムは「ネストされたループが終了しました。」をプリントします。

プログラムの出力:

i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
ネストされたループが終了しました。

もしこのラベルがない場合(単に break; と書いた場合)、i=2, j=2 のときには内側のループだけが終了し、外側のループは i=3 のラウンドへと継続されます。

2. continue 文

continue 文は、現在のイテレーションの残りのコードをスキップし、即座にループの次のイテレーションを開始するために使用されます。break がループ全体を終了させるのに対し、continue は「今回はここまでにして、次の回を始めよう」という意味になります。

2.1 基本的な使い方

特定の条件に基づいて、ループ内の特定の操作だけを避けたい場合に continue は非常に有用です。

public class ContinueExample {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i % 2 == 0) {
                continue; // 偶数の場合、今回の残りのコードをスキップして次のイテレーションへ
            }
            System.out.println("i = " + i);
        }
        System.out.println("ループが終了しました。");
    }
}

コードの解説:

  1. 1 から 10 までの for ループがあります。
  2. ループ内で、剰余演算子 (%) を使用して i が偶数かどうかをチェックします。
  3. i が偶数の場合、continue 文がトリガーされます。
  4. continue は、後続の System.out.println コードを即座にスキップします。
  5. ループは次のイテレーションに進みます(例:i=2 から i=3 へ)。

プログラムの出力:

i = 1
i = 3
i = 5
i = 7
i = 9
ループが終了しました。

すべての偶数 (2, 4, 6, 8, 10) がプリントされず、奇数のみが出力されていることがわかります。

2.2 ネストされたループにおける continue (ラベル付き continue)

break と同様に、continue もデフォルトでは最も内側のループにのみ作用します。内側のループから外側のループを強制的に次のイテレーションに進ませたい場合は、ラベル付き continue を使用します。

public class NestedContinueExample {
    public static void main(String[] args) {
        outerLoop: // 外側のループにラベルを定義
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= 3; j++) {
                if (i == 2 && j == 2) {
                    continue outerLoop; // 内側の現在の操作を中断し、外側のループを次のラウンドへ進ませる
                }
                System.out.println("i = " + i + ", j = " + j);
            }
        }
        System.out.println("ネストされたループが終了しました。");
    }
}

コードの解説:

  1. 外側のループに outerLoop ラベルを付けます。
  2. 内側のループにおいて、i=2, j=2 のときに continue outerLoop; をトリガーします。
  3. このコードは「現在の内側ループのアクションを即座に停止し、outerLoop ラベルが付いた外側ループの次のイテレーションを開始せよ」と伝えます。

プログラムの出力:

i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
i = 3, j = 1
i = 3, j = 2
i = 3, j = 3
ネストされたループが終了しました。

i=2 のとき、j=1 は正常に出力されていますが、j=2 を出力するはずのタイミングでラベル付き continue がトリガーされ、その時の内側ループの残りのステップが破棄され、外側ループが i=3 へ進んでいる点に注目してください。

3. 実践的なユースケースのデモンストレーション

3.1 無効なデータのフィルタリング (continue の活用)

ユーザー ID のリストを処理しており、正の ID のみを処理し、0 以下の異常な ID をスキップしたいシナリオを想定します。

public class ProcessUserIds {
    public static void main(String[] args) {
        int[] userIds = {101, -5, 205, 0, 302, -1, 400};
        
        for (int userId : userIds) {
            if (userId <= 0) {
                continue; // 無効なユーザー ID をスキップして処理しない
            }
            System.out.println("ユーザー ID を処理中: " + userId);
            // ここにユーザー処理の複雑なロジックがあると仮定...
        }
        System.out.println("ユーザー ID の処理が完了しました。");
    }
}

continue を使用することで、無効な ID (-5, 0, -1) が直接無視され、有効な正の ID のみが出力・処理されます。

3.2 ターゲット要素の検索 (break の活用)

もう一つの典型的なシナリオは、配列(アレイ)から特定のターゲットを検索し、見つかった瞬間に残りの要素をチェックする必要がなくなるケースです。

public class SearchArray {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30, 40, 50};
        int target = 30;
        boolean found = false; // ターゲットが見つかったかどうかをマーク
        
        for (int number : numbers) {
            System.out.println("数字をチェック中: " + number);
            if (number == target) {
                found = true;
                break; // 見つかった!直ちにループを抜けてパフォーマンスを節約
            }
        }
        
        if (found) {
            System.out.println("配列内でターゲットが見つかりました: " + target);
        } else {
            System.out.println("配列内にターゲットは存在しません: " + target);
        }
    }
}

プログラムが 30 を検出した時点で found が true になり、その後 break によってループを脱出します。そのため、コンソールには「数字をチェック中: 40」や 50 はプリントされません。