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 */ }
案の定ハッシュ値を使っていて、次のような流れで処理していることが判ります。
- ローカル変数2を-1で初期化し(60~61行目)、
- 引数のString型のハッシュ値を取得し(63行目)、
- 1つ目のlookupswitchでハッシュ値が同値なら、さらにString.equals()で評価し(64~84行目)、
- 評価結果をローカル変数2に数値を代入して(0または1)(75、76、82、83行目)、
- 2つ目のlookupswitchで、ローカル変数2の値に応じた処理を行う(84~97行目)
2つのlookupswitchを使って条件分岐しているのは意外でした。