2011/09/23

"Strings in switch" in Java 7 (JSR 334)

Java 7で導入されたJSR 334には、「Strings in switch」がありますが、switch構文でString型も扱えるようになっています。それがどのようにコンパイルされるのかを調べてみました。



package a;
public class E {
  public static int a(String s){
    switch(s){
    case "a":
      return 1;
    case "b":
      return 2;
    default:
      return 3;
    }
  }
}



上記ソースをjavacでコンパイルして、javap -verboseしてみました。


Classfile /E:/tmp/java/java7/bin/a/E.class
  Last modified 2011/09/23; size 491 bytes
  MD5 checksum 525f1ebe51742f599543582cb206c23c
  Compiled from "E.java"
public class a.E
  SourceFile: "E.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER

Constant pool:
   #1 = Methodref          #7.#18         //  java/lang/Object."<init>":()V
   #2 = Methodref          #19.#20        //  java/lang/String.hashCode:()I
   #3 = String             #12            //  a
   #4 = Methodref          #19.#21        //  java/lang/String.equals:(Ljava/lang/Object;)Z
   #5 = String             #22            //  b
   #6 = Class              #23            //  a/E
   #7 = Class              #24            //  java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               a
  #13 = Utf8               (Ljava/lang/String;)I
  #14 = Utf8               StackMapTable
  #15 = Class              #25            //  java/lang/String
  #16 = Utf8               SourceFile
  #17 = Utf8               E.java
  #18 = NameAndType        #8:#9          //  "<init>":()V
  #19 = Class              #25            //  java/lang/String
  #20 = NameAndType        #26:#27        //  hashCode:()I
  #21 = NameAndType        #28:#29        //  equals:(Ljava/lang/Object;)Z
  #22 = Utf8               b
  #23 = Utf8               a/E
  #24 = Utf8               java/lang/Object
  #25 = Utf8               java/lang/String
  #26 = Utf8               hashCode
  #27 = Utf8               ()I
  #28 = Utf8               equals
  #29 = Utf8               (Ljava/lang/Object;)Z
{
  public a.E();
    flags: ACC_PUBLIC

    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return        
      LineNumberTable:
        line 3: 0

  public static int a(java.lang.String);
    flags: ACC_PUBLIC, ACC_STATIC

    Code:
      stack=2, locals=3, args_size=1
         0: aload_0       
         1: astore_1      
         2: iconst_m1     
         3: istore_2      
         4: aload_1       
         5: invokevirtual #2                  // Method java/lang/String.hashCode:()I
         8: lookupswitch  { // 2

                      97: 36

                      98: 50
                 default: 61
            }
        36: aload_1       
        37: ldc           #3                  // String a
        39: invokevirtual #4                  // 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           #5                  // String b
        53: invokevirtual #4                  // 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: 90
                 default: 92
            }
        88: iconst_1      
        89: ireturn       
        90: iconst_2      
        91: ireturn       
        92: iconst_3      
        93: ireturn       
      LineNumberTable:
        line 5: 0
        line 7: 88
        line 9: 90
        line 11: 92
      StackMapTable: number_of_entries = 6
           frame_type = 253 /* append */
             offset_delta = 36
        locals = [ class java/lang/String, int ]
           frame_type = 13 /* same */
           frame_type = 10 /* same */
           frame_type = 26 /* same */
           frame_type = 1 /* same */
           frame_type = 1 /* same */

}



案の定ハッシュ値を使っていて、次のような流れで処理していることが判ります。

  1. ローカル変数2を-1で初期化し(60~61行目)、
  2. 引数のString型のハッシュ値を取得し(63行目)、
  3. 1つ目のlookupswitchでハッシュ値が同値なら、さらにString.equals()で評価し(64~84行目)、
  4. 評価結果をローカル変数2に数値を代入して(0または1)(75、76、82、83行目)、
  5. 2つ目のlookupswitchで、ローカル変数2の値に応じた処理を行う(84~97行目)

2つのlookupswitchを使って条件分岐しているのは意外でした。

0 件のコメント:

コメントを投稿