Java中String的switch-case字节码与等价实现代码
switch-case 语句在 case 比较稀疏的情况下,编辑器会使用 lookupswitch 指令来实现,反之,编辑器会使用 tableswitch 来实现。
对于 String 的 switch-case 中会将 case 后面的string转化成哈希值,而哈希值一般是比较稀疏的,所以选择 lookupswitch 来作为switch-case来实现。
拿下面例子说明:
public int test(String name) {
switch (name) {
case "Java":
return 100;
case "Kotlin":
return 200;
default:
return -1;
}
}
上面代码的字节码如下:
0 : aload_1
1 : astore_2 // 将name赋值给局部变量表下标为2的变量
2 : icount_m1 // -1
3 : istore_3 // 初始化局部变量表中位置为3的变量为-1
4 : aload_2
5 : invokevirtual #2 // Method java/lang/String.hashCode:()
8 : lookupswitch {
-2041707231: 50 // 对应 "Kotlin".hashCode()
2301506: 36 // 对应 "Java".hashCode()
default: 61
}
36 : aload_2
37 : ldc #3 // String Java 从常量池压栈到栈顶
39 : invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)z
42 : ifeq 61 // 如果等于 0 (false)则跳到 对应字节码行处
45 : iconst_0
46 : istore_3
47 : goto 61
50 : aload_2
51 : ldc #5 // String Kotlin 从常量池压栈到栈顶·
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 {
0: 88
1: 91
default: 95
}
88 : bipush 100 // 将范围在-128~127 的整形值压栈到栈顶
90 : ireturn
91 : sipush 200 // 将范围在-32768~32767 的整形值压栈到栈顶
94 : ireturn
95 : iconst_m1 -1
96 : ireturn
结合字节码我们可以推出其的等价实现代码:
public int test_translate(String name) {
String tmpName = name;
int matchIndex = -1;
switch (tmpName.hashCode()) {
case -2041707231:
if ( tmpName.equals("Kotlin"))
matchIndex = 1;
break;
case 2301506:
if ( tmpName.equals("Java"))
matchIndex = 0;
break;
default:
break;
}
switch (matchIndex) {
case 0:
return 100;
case 1:
return 200;
default:
return -1;
}
}
对字符串去哈希值时会面临冲突问题,比如 Aa 和 BB 的hashCode 都是2112。对代码稍微修改即可,如下:
public int test_translate(String name) {
String tmpName = name;
int matchIndex = -1;
switch (tmpName.hashCode()) {
case 2112:
if ( tmpName.equals("BB"))
matchIndex = 1;
else if ( tmpName.equals("Aa"))
matchIndex = 0;
break;
default:
break;
}
switch (matchIndex) {
case 0:
return 100;
case 1:
return 200;
default:
return -1;
}
}