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つしかできませんでした。