文字列は、スイッチに関する数値型で、常にlookupswitchにコンパイル?

ルシオ:

与えられたかどうか、次のコードに戻り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文のコンパイルには使用していますtableswitchlookupswitchの指示を。

tableswitchlookupswitch命令はのみで動作する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ラベルの値)...

進むtableswitchlookupswitch JMSを言います:

tableswitchのスイッチの場合は、効率的に目標オフセットのテーブルへのインデックスとして表すことができるときに命令が使用されます。スイッチの例が疎である場合(...)、のテーブル表現tableswitchの命令は、空間的に非効率的になります。lookupswitchの命令を用いてもよいです。

私はこの権利を取得する場合は、より多くの例は、スパースよりおそらくなっlookupswitchが使用されます。

質問2:
しかし、バイトコードを見て:
2つの文字列の場合は、コンパイルするのに十分な疎なswitchlookupswitchまたは上のすべてのスイッチはなりStringにコンパイルすることlookupswitch

ホルガー:

仕様は、コンパイルする方法を教えてくれないswitchコンパイラまでだと、文を。

その点では、JVMS声明、「その他の数値型は、型に狭くしなければならないintで使用するためswitchのJavaプログラミング言語は、このような変換を行いますと言ってもそれはありません」StringまたはEnum数値型です。すなわちlongfloatdouble されている数値型が、それらを使用するためのサポートがないswitchJavaプログラミング言語の文が。

だから、言語仕様はと言うswitch以上Stringサポートされています、したがって、コンパイラは、バイトコードにそれらをコンパイルする方法を見つける必要があります。ハッシュコードのような不変性を使用する一般的な解決策であるが、原理的には、長さ又は任意の文字のような他の特性はよくとして使用することができます。

で説明したように「ストリング上のスイッチは、2つのスイッチにコンパイルなぜ」および「Javaの7文字列スイッチは、逆コンパイル:予期しない命令」、javacコンパイル時に、現在のバイトコードレベルで2つのスイッチ命令を生成switchオーバーString値(ECJはまた、2つの命令を生成するが、詳細は異なっていてもよいです) 。

その後、コンパイラは選択のいずれか、しなければならないlookupswitchtableswitchの指示。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第二のスイッチのため。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=191537&siteId=1