与えられたかどうか、次のコードに戻りString s
、ハードコードされた文字列の他に等しいです。この方法は、使用していますswitch
そうする-statementを:
public class SwitchOnString {
public static boolean equalsAny(String s) {
switch (s) {
case "string 1":
return true;
case "string 2":
return true;
default:
return false;
}
}
}
よると、Java仮想マシン仕様(JMS)3.10コンパイルスイッチ」
switch文のコンパイルには使用していますtableswitchとlookupswitchの指示を。
又
tableswitchとlookupswitch命令はのみで動作する
int
データ。
私は章3.10を読んでどこにでも見つけることができませんでしたString
言及しました。
近くに間接的に来るだけで一つの文は次のようになります。
その他の数値型は、スイッチで使用するためにint型に狭くする必要があります。
質問1:
ですString
数値型も、この文脈では?それとも私が何かを逃したのですか?
javap -c
クラスのSwitchOnString
ショー:
Compiled from "SwitchOnString.java"
public class playground.SwitchOnString {
public playground.SwitchOnString();
...
public static boolean equalsAny(java.lang.String);
Code:
0: aload_0
1: dup
2: astore_1
3: invokevirtual #16 // Method java/lang/String.hashCode:()I
6: lookupswitch { // 2
1117855161: 32
1117855162: 44
default: 60
}
...
}
明らかhashCode
値は次のように使用されているint
の-keys case
秒。これはおそらく一致します。
lookupswitch命令ペアの
int
キー(caseラベルの値)...
進むtableswitchとlookupswitch JMSを言います:
tableswitchのスイッチの場合は、効率的に目標オフセットのテーブルへのインデックスとして表すことができるときに命令が使用されます。スイッチの例が疎である場合(...)、のテーブル表現tableswitchの命令は、空間的に非効率的になります。lookupswitchの命令を用いてもよいです。
私はこの権利を取得する場合は、より多くの例は、スパースよりおそらくなっlookupswitchが使用されます。
質問2:
しかし、バイトコードを見て:
2つの文字列の場合は、コンパイルするのに十分な疎なswitch
にlookupswitch?または上のすべてのスイッチはなりString
にコンパイルすることlookupswitch?
仕様は、コンパイルする方法を教えてくれないswitch
コンパイラまでだと、文を。
その点では、JVMS声明、「その他の数値型は、型に狭くしなければならないint
で使用するためswitch
のJavaプログラミング言語は、このような変換を行いますと言ってもそれはありません」String
またはEnum
数値型です。すなわちlong
、float
とdouble
されている数値型が、それらを使用するためのサポートがないswitch
Javaプログラミング言語の文が。
だから、言語仕様はと言うswitch
以上String
サポートされています、したがって、コンパイラは、バイトコードにそれらをコンパイルする方法を見つける必要があります。ハッシュコードのような不変性を使用する一般的な解決策であるが、原理的には、長さ又は任意の文字のような他の特性はよくとして使用することができます。
で説明したように「ストリング上のスイッチは、2つのスイッチにコンパイルなぜ」および「Javaの7文字列スイッチは、逆コンパイル:予期しない命令」、javac
コンパイル時に、現在のバイトコードレベルで2つのスイッチ命令を生成switch
オーバーString
値(ECJはまた、2つの命令を生成するが、詳細は異なっていてもよいです) 。
その後、コンパイラは選択のいずれか、しなければならないlookupswitch
かtableswitch
の指示。javac
使用しないtableswitch
番号がスパースではなく、文が二つ以上のcaseラベルがある場合にのみとき。
だから私は、次のメソッドをコンパイルするとき:
public static char two(String s) {
switch(s) {
case "a": return 'a';
case "b": return 'b';
}
return 0;
}
I GET
public static char two(java.lang.String);
Code:
0: aload_0
1: astore_1
2: iconst_m1
3: istore_2
4: aload_1
5: invokevirtual #9 // Method java/lang/String.hashCode:()I
8: lookupswitch { // 2
97: 36
98: 50
default: 61
}
36: aload_1
37: ldc #10 // String a
39: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 61
45: iconst_0
46: istore_2
47: goto 61
50: aload_1
51: ldc #12 // String b
53: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 61
59: iconst_1
60: istore_2
61: iload_2
62: lookupswitch { // 2
0: 88
1: 91
default: 94
}
88: bipush 97
90: ireturn
91: bipush 98
93: ireturn
94: iconst_0
95: ireturn
しかし、私は、コンパイル時に、
public static char three(String s) {
switch(s) {
case "a": return 'a';
case "b": return 'b';
case "c": return 'c';
}
return 0;
}
I GET
public static char three(java.lang.String);
Code:
0: aload_0
1: astore_1
2: iconst_m1
3: istore_2
4: aload_1
5: invokevirtual #9 // Method java/lang/String.hashCode:()I
8: tableswitch { // 97 to 99
97: 36
98: 50
99: 64
default: 75
}
36: aload_1
37: ldc #10 // String a
39: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 75
45: iconst_0
46: istore_2
47: goto 75
50: aload_1
51: ldc #12 // String b
53: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 75
59: iconst_1
60: istore_2
61: goto 75
64: aload_1
65: ldc #13 // String c
67: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
70: ifeq 75
73: iconst_2
74: istore_2
75: iload_2
76: tableswitch { // 0 to 2
0: 104
1: 107
2: 110
default: 113
}
104: bipush 97
106: ireturn
107: bipush 98
109: ireturn
110: bipush 99
112: ireturn
113: iconst_0
114: ireturn
理由は明らかではありませんjavac
。この選択肢となります。ながらtableswitch
と比較して、より高いベース占有領域(一つの追加の32ビット・ワード)を有しlookupswitch
、それはまだも2つのため、バイトコードに短くなるであろうcase
ラベルシナリオ。
しかし、判決の一貫性が常に同じ値の範囲を持っていますが、コンパイルになり番目のステートメント、で示されることができるlookupswitch
か、tableswitch
ラベルの数にのみ依存します。だから、本当にまばらな値を使用している場合:
public static char three(String s) {
switch(s) {
case "a": return 'a';
case "b": return 'b';
case "": return 0;
}
return 0;
}
それはにコンパイル
public static char three(java.lang.String);
Code:
0: aload_0
1: astore_1
2: iconst_m1
3: istore_2
4: aload_1
5: invokevirtual #9 // Method java/lang/String.hashCode:()I
8: lookupswitch { // 3
0: 72
97: 44
98: 58
default: 83
}
44: aload_1
45: ldc #10 // String a
47: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
50: ifeq 83
53: iconst_0
54: istore_2
55: goto 83
58: aload_1
59: ldc #12 // String b
61: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
64: ifeq 83
67: iconst_1
68: istore_2
69: goto 83
72: aload_1
73: ldc #13 // String
75: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
78: ifeq 83
81: iconst_2
82: istore_2
83: iload_2
84: tableswitch { // 0 to 2
0: 112
1: 115
2: 118
default: 120
}
112: bipush 97
114: ireturn
115: bipush 98
117: ireturn
118: iconst_0
119: ireturn
120: iconst_0
121: ireturn
使用lookupswitch
スパースハッシュコードのため、しかし、tableswitch
第二のスイッチのため。