字节码层面理解Switch

1. 源码如下,test(int)方法使用switch语法,根据参数a的值返回不同字符串。

public class MySwitchTest {

    public String test(int a){
        switch (a){
            case 1:
                return "a";
            case 2:
                return "b";
            case 3:
                return "c";
            case 5:
                return "e";
            default:
                return "f";
        }
    }

}

2. javac -g MySwitchTest.java  得到MySwitchTest.class

javap -v -p MySwitchTest.class  可以得到test(int)方法的字节码如下

  public java.lang.String test(int);
    descriptor: (I)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: iload_1
         1: tableswitch   { // 1 to 5

                       1: 36

                       2: 39

                       3: 42

                       4: 48

                       5: 45
                 default: 48
            }
        36: ldc           #2                  // String a
        38: areturn
        39: ldc           #3                  // String b
        41: areturn
        42: ldc           #4                  // String c
        44: areturn
        45: ldc           #5                  // String e
        47: areturn
        48: ldc           #6                  // String f
        50: areturn

仔细观察,源码中的case分支并没有4,但是字节码中出现了4,这是为什么呢?编译器会对 case 的值做分析,如果 case 的值比较紧凑,中间有少量断层或者没有断层,会采用 tableswitch 来实现 switch-case,有断层的会生成一些虚假的 case 帮忙补齐连续,这样可以将所有的分支地址存储在数组table中,就可以实现 O(1) 时间复杂度的查找:因为 case 已经被补齐为连续的,通过游标就可以一次找到。

寻找分支的逻辑伪代码如下,这段代码时间复杂度就是O(1)

int val = ...; //根据val的值来寻找分支
int pc;    //分支所在的索引
if(val < low || val > {
    pc = default; //pc指向default分支
} else {
    pc = table[val-low]; //从table中寻找分支
}

假如case的值断层严重呢?

public class MySwitchTest {

    public String test(int a){
        switch (a){
            case 100:
                return "a";
            case 10:
                return "b";
            case 101:
                return "c";
            case 1:
                return "e";
            default:
                return "f";
        }
    }

}

对应的字节码如下

public java.lang.String test(int);
    descriptor: (I)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: iload_1
         1: lookupswitch  { // 4

                       1: 44

                      10: 47

                     100: 50

                     101: 53
                 default: 56
            }
        44: ldc           #2                  // String a
        46: areturn
        47: ldc           #3                  // String b
        49: areturn
        50: ldc           #4                  // String c
        52: areturn
        53: ldc           #5                  // String e
        55: areturn
        56: ldc           #6                  // String f
        58: areturn

仔细观察,这次没有使用指令tableswitch,而是改为lookupswitch,因为如果继续使用tableswitch,那么需要补齐超级多的case,反而会造成性能下降。

再仔细观察,源码中case分支使无序的,到了字节码中,case值变成有序的了,这样有什么好处呢?因为case值是有序的,在寻找分支时,通过二分法就可以将寻找分支的时间复杂度降为O(logN)


总结,switch语句,在case值比较稀疏时,采用指令lookupswitch,时间复杂度为O(logN),当case值比较紧密时,时间复杂度是O(1),相比于if的O(N)性能要好很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值