条件分岐 (switch)

switch 文 (switch statement)

switch文(switch statement)は、多くの選択肢から1つを選んで実行する。

たとえば、

if ( n == 2 ) 
  B1
else if ( n == 5 )
  B2
else
  B3

は、nの値が2ならB1を、5ならB2を、それ以外ならB3を、それぞれ実行するif文であるが、これをswitch文で書けば次のようになる。

switch (n) {
  case 2:
    B1
    break;
  case 5:
    B2
    break;
  default:
    B3
}

このswitch文では、まずnの値が求められ、その値が2であれば、case 2:のあとに書かれた文の列が実行され、その値が5であれば、case 5:のあとに書かれた文の列が実行され、そのどれでもない場合はdefault:のあとに書かれた文の列が実行される。

break;

はbreak文(break statement)と呼ばれ、これを実行するとこのswitch文の実行が終わる。もし、B1の後のbreak文がないと、nの値が2のときにはB1を実行した後でB2が実行されてしまう。B3のあとにbreak文が書いてないのは、B3がswitch文の最後にあるから、break文がなくてもB3を実行したらこのswitch文の実行が終わるからである。

B3の後に明示的にbreak文を書いても良い。

switch (n) {
  case 2:
    B1
    break;
  case 5:
    B2
    break;
  default:
    B3
    break;
}

上記のswitch文からbreak文を取り除くと以下のような形になる。

switch (n) {
  case 2:
    B1
  case 5:
    B2
  default:
    B3
}

これは、次のようなif-elseの形で書き直すことができる。

if ( n == 2 ) {
  B1
  B2
  B3
}
else if ( n == 5 ) {
  B2
  B3
}
else {
  B3
}

一般に、switch文は次の形である。

switch (整数型の式) {
  case 整数型の定数:
    文の列
  case 整数型の定数:
    文の列
  ...
  default:
    文の列
}

このswitch文は次のように実行される。まず「整数型の式」の値が計算され、その値と等しい「整数型の定数」の書かれているcaseが選ばれて(この定数はcaseのラベルとよばれる)、その後に書かれている「文の列」が実行される。もし、「文の列」の最後にbreak文がないと、続けてその次のcaseの「文の列」を実行する。「整数型の式」の値と等しい「整数型の定数」がなければ、「default:」のあとの「文の列」が実行される。この最後の「default:」とそのあとの「文の列」は書かなくてもよい。しかし、それが書いてない場合で、「整数型の式」の値がどのcaseの値とも一致しない場合は、どの「文の列」も実行せずにswitch文の実行が終了してしまう。そのようなことが起きないように、いつでもdefault:を書いておくのが安全である。

case~の末尾は「:(コロン)」であることに注意すること。

caseラベルの定数式

caseラベルには整数型の定数を書くといったが、より正確には定数式を書くことが出来る。定数式は定数だけを使った式で変数などを含まないものである。たとえば

3 + 5 + 9

は定数式であり、次の2つのcaseラベルは同じになってしまう。

case 3 + 5 + 9:
case 17:

同じラベルを持つcaseが一つのswitch文に含まれていると、どちらを優先すればいいのか曖昧になってしまうため、コンパイルエラーとなる。

switch 文の例

switchを用いる例として、以下のようなプログラムが挙げられる。

package j1.lesson06;

import java.io.*;

public class Switch3 {

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        System.out.print("整数を入力してください:");
        int input = Integer.parseInt(reader.readLine());

        // 入力された値を用いて switch
        switch (input) {
            // 入力された値が 0
            case 0:
                System.out.println("0が入力されました");
                break;

            // 入力された値が 1
            case 1:
                System.out.println("入力された値は1");
                break;

            // 入力された値が 2
            case 2:
                System.out.println("2です");
                break;

            // 入力された値がそれ以外
            default:
                System.out.println("0でも1でも2でもありません");
        }
    }
}

上記のプログラムは、入力された値が0から2のときはそれぞれの場合に応じたメッセージを表示し、それ以外の場合は「0でも1でも2でもありません」と出力する。

caseの扱い

caseラベルは次のように使用することもできる。

package j1.lesson06;

import java.io.*;

public class MultiCase {

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("整数を入力してください:");
        int input = Integer.parseInt(reader.readLine());

        // 10 で割った余りについて調べる
        switch (input % 10) {
            // 10 で割り切れる
            case 0:
                System.out.println("10の倍数です");
                break;

            // 10 で割った余りが 1 または 9
            case 1:
            case 9:
                System.out.println("もう少しで10の倍数でした");
                break;

            // 10 で割った余りが 5
            case 5:
        	     System.out.println("10の倍数ではありませんが5の倍数です");
                break;

            // それ以外
            default:
                System.out.println("10の倍数ではありません");
        }
    }
}

これは

case 1:
case 9:

と、2つのcaseを並べることによって、switchで指定した値が 1 または 9 の時に同じ処理をさせることができる。例では2つを並べたが、それ以上のcaseを並べても良い。

しかし、余りに多くのcaseを並べると読みにくいプログラムになってしまう可能性がある。その場合は if-else の連鎖によって実現したほうが読みやすい場合が多い。

switch 以外での break

switch文で使用したbreakは、switch文以外でもいくつかの場面で使用できる。

for 文での break

for 文を用いた繰り返しの中で break 文を使用すると、繰り返しをその時点で終了させることができる。

for (int i = 0; i < 100; i++) {
    if (i == 1) {
        break;
    }
    System.out.println(i);
}

上記のプログラムの断片を実行した場合、

0

とだけ表示して for 文を抜けることになる。

while 文での break

while 文の中で break 文を使用すると、for 文と同様に繰り返しをその時点で終了させることができる。

int hoge = 1;
while (hoge <= 10000) {
  hoge *= 2;
  break;
}

break 文の注意

次のプログラムはどのような動きをするだろうか。

package j1.lesson06;

public class ForSwitch {

    public static void main(String[] args) {
        for (int i = 0; i <= 5; i++) {
            switch (i) {
                case 0:
                case 2:
                case 4:
                    System.out.println("偶数");
                    // この break は switch を抜ける break
                    break;
                
                case 1:
                case 3:
                case 5:
                    System.out.println("奇数");
                    // この break は switch を抜ける break
                    break;
                
                default:
                // ここには来ない
            }
        }
    }
}

上記のプログラム内に出てくる break 文は、コメントにもあるように switch 文を抜けるための break である。

switch から その外側にある for による繰り返しを終了させる方法もあるが、ここでは紹介しない (興味があれば調べてみよ)。

ネストした繰り返しの中で break 文を使用すると、一番内側の繰り返しだけ終了させることになる。

package j1.lesson06;
public class ForFor {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            for (int j = 1; j <= 5; j++) {
                System.out.println(i + j);
                break;
            }
        }
    }
}

上記の例を実行させた場合、表示される結果は以下のようになる。

2
3
4
5
6

繰り返し内での break を使う場面

繰り返しの中でbreak文を使用すると、繰り返しの出口が2つ以上できてしまうことになる。

for (int i = 1; i <= 10 ; i++) {
    if (i == 5) {
        break; // 繰り返しの出口 (1)
    }
    else {
        System.out.println(i);
    }
} // 繰り返しの出口 (2)

上記のような簡単な繰り返しならば問題ないが、複雑な繰り返しの様々な場所から脱出すると、繰り返しの条件が見づらくなってしまう。

繰り返し構文にはプログラムのバグ (不具合) が混入しやすい。以下の2つの繰り返しは同じ動作をするが、どちらが読みやすいだろうか?

for (int i = 1; i <= 10 ; i++) {
    System.out.println(i);
}
for (int i = 1; i <= 11 ; i++) {
    System.out.println(i);
    if (i >= 10) {
        break;
    }
}

breakを使用すると、繰り返し条件の表明が点在してしまい複雑になってしまう。break文を使用するもっともな理由がない限りは使用すべきではない。

例えば、以下のような場面では良く使う。

次のプログラムは、「valueが素数であるか判定する」簡単なプログラムである。

int divisor = 0;
for (int i = 2; i < value; i++) {
    // 割り切れるか調べる
    if (value % i == 0) {
        divisor = i;
        // 割り切れたので終了
        break;
    }
}
// 割り切れる値がなかった
if (divisor == 0) {
    System.out.println(value + " is prime");
}
// 1 と 自分自身以外で割り切れた
else {
    System.out.println(value + " is not prime");
}

上記の例のように、繰り返しの中で「何かを一つでも見つける」というようなプログラムを書く場合、見つけるべきものを検出したら break してよい。

これを次のように書くと分かりにくくなる場合がある。

int divisor = 0;
for (int i = 2; i < value && divisor == 0; i++) {
    if (value % i == 0) {
        divisor = i;
    }
}
...

forは「指定回数の繰り返し」に専念すべきであり、余計な条件を加えると混乱してしまう可能性がある。