Java SE 7 (1) - 文字列switchのからくり
遅れ馳せ乍ら、Java7について自分なりにまとめてみます。
最初は概論から入るのが定石でありましょうが、現状は何時投稿できるか分からない状況ですので、小出しにしていくことにします。
まずは、"Strings in switch Statements"から。
これは珍しくピーンと来たので、確認をしてみます。
final class Main { void f(String s) { switch (s) { case "aa": System.out.println("1"); break; case "bb": System.out.println("2"); break; default: } } }
これをコンパイルして、javapでディスアセンブルしてみます。
Compiled from "1.java" final class Main { Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return void f(java.lang.String); Code: 0: aload_1 1: astore_2 2: iconst_m1 3: istore_3 4: aload_2 5: invokevirtual #2 // Method java/lang/String.hashCode:()I 8: lookupswitch { // 2 3104: 36 3136: 50 default: 61 } 36: aload_2 37: ldc #3 // String aa 39: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 42: ifeq 61 45: iconst_0 46: istore_3 47: goto 61 50: aload_2 51: ldc #5 // String bb 53: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 56: ifeq 61 59: iconst_1 60: istore_3 61: iload_3 62: lookupswitch { // 2 0: 88 1: 99 default: 110 } 88: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 91: ldc #7 // String 1 93: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 96: goto 110 99: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 102: ldc #9 // String 2 104: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 107: goto 110 110: return }
ふむふむ。どうやら文字列のhashCodeを使ってswitchで分岐してから、その中でString#equalsで等しいことをチェック。等しければ0からの連番となる固定値を割り振っていく。この値で再度switchする、という仕組みのようです。
Java6以前のままで同様の処理を書いたら、こんな感じになるのかな。
final class Main { void f(String s) { int a = -1; switch (s.hashCode()) { case 3104: // "aa"のhashCode if (s.equals("aa")) { a = 0; } break; case 3136: // "bb"のhashCode if (s.equals("bb")) { a = 1; } break; default: } switch (a) { case 0: System.out.println("1"); break; case 1: System.out.println("2"); break; default: } } }
javapしてみる。
Compiled from "1.java" final class Main { Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return void f(java.lang.String); Code: 0: iconst_m1 1: istore_2 2: aload_1 3: invokevirtual #2 // Method java/lang/String.hashCode:()I 6: lookupswitch { // 2 3104: 32 3136: 46 default: 60 } 32: aload_1 33: ldc #3 // String aa 35: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 38: ifeq 60 41: iconst_0 42: istore_2 43: goto 60 46: aload_1 47: ldc #5 // String bb 49: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 52: ifeq 60 55: iconst_1 56: istore_2 57: goto 60 60: iload_2 61: lookupswitch { // 2 0: 88 1: 99 default: 110 } 88: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 91: ldc #7 // String 1 93: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 96: goto 110 99: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 102: ldc #9 // String 2 104: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 107: goto 110 110: return }
だいたい一緒になりました。
最後に、もしhashCodeが同じ文字列を使ったらどうなるかを試します。今度はJava6以前のコードとまとめてやってしまいましょう。
final class Main { void f(String s) { switch (s) { case "an": System.out.println("1"); break; case "c0": System.out.println("2"); break; default: } } void f2(String s) { int a = -1; switch (s.hashCode()) { case 3117: if (s.equals("an")) { a = 0; } else if (s.equals("c0")) { a = 1; } break; default: } switch (a) { case 0: System.out.println("1"); break; case 1: System.out.println("2"); break; default: } } }
Compiled from "2.java" final class Main { Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return void f(java.lang.String); Code: 0: aload_1 1: astore_2 2: iconst_m1 3: istore_3 4: aload_2 5: invokevirtual #2 // Method java/lang/String.hashCode:()I 8: lookupswitch { // 1 3117: 28 default: 53 } 28: aload_2 29: ldc #3 // String c0 31: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 34: ifeq 42 37: iconst_1 38: istore_3 39: goto 53 42: aload_2 43: ldc #5 // String an 45: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 48: ifeq 53 51: iconst_0 52: istore_3 53: iload_3 54: lookupswitch { // 2 0: 80 1: 91 default: 102 } 80: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 83: ldc #7 // String 1 85: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 88: goto 102 91: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 94: ldc #9 // String 2 96: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 99: goto 102 102: return void f2(java.lang.String); Code: 0: iconst_m1 1: istore_2 2: aload_1 3: invokevirtual #2 // Method java/lang/String.hashCode:()I 6: lookupswitch { // 1 3117: 24 default: 52 } 24: aload_1 25: ldc #5 // String an 27: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 30: ifeq 38 33: iconst_0 34: istore_2 35: goto 52 38: aload_1 39: ldc #3 // String c0 41: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 44: ifeq 52 47: iconst_1 48: istore_2 49: goto 52 52: iload_2 53: lookupswitch { // 2 0: 80 1: 91 default: 102 } 80: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 83: ldc #7 // String 1 85: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 88: goto 102 91: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 94: ldc #9 // String 2 96: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 99: goto 102 102: return }
やはり、caseが1つしかできませんでした。