Java中String的switch-case字节码与等价实现代码

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;
    }
}
posted @ 2021-01-14 11:27  狡猾的狐狸科  阅读(410)  评论(0编辑  收藏  举报