java中对switch的处理

        switch是控制选择的一种方式,编译器生成代码时可以对这种结构进行特定的优化,从而产生效率比较高的代码。在java中,编译器根据分支的情况,分别产生tableswitch,lookupswitch两中情况,其中tableswitch适用于分支比较集中的情况,而lookupswitch适用与分支比较稀疏的情况。不过怎么算稀疏,怎么算集中就是编译器的决策问题了,这里不做深入的分析。

简单的找几个例子。

例一:

public class Test { public static void main(String[] args) { int i = 3; switch (i) { case 0: System.out.println("0"); break; case 1: System.out.println("1"); break; case 3: System.out.println("3"); break; case 5: System.out.println("5"); break; case 10: System.out.println("10"); break; case 13: System.out.println("13"); break; case 14: System.out.println("14"); break; default: System.out.println("default"); break; } } }

反汇编代码可以发现其跳转表的结构:

Code: 0: iconst_3 1: istore_1 2: iload_1 3: tableswitch{ //0 to 14 0: 76; 1: 87; 2: 153; 3: 98; 4: 153; 5: 109; 6: 153; 7: 153; 8: 153; 9: 153; 10: 120; 11: 153; 12: 153; 13: 131; 14: 142; default: 153 } 76: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 79: ldc #3; //String 0 81: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 84: goto 161 87: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 90: ldc #5; //String 1 92: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 95: goto 161 98: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 101: ldc #6; //String 3 103: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 106: goto 161 109: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 112: ldc #7; //String 5 114: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 117: goto 161 120: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 123: ldc #8; //String 10 125: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 128: goto 161 131: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 134: ldc #9; //String 13 136: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 139: goto 161 142: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 145: ldc #10; //String 14 147: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 150: goto 161 153: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 156: ldc #11; //String default 158: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 161: return

其中的 3:tableswitch{ //0 to 14
0: 76;
1: 87;
2: 153;
3: 98;
4: 153;
5: 109;
6: 153;
7: 153;
8: 153;
9: 153;
10: 120;
11: 153;
12: 153;
13: 131;
14: 142;
default: 153 }
就是跳转表,对于tableswitch指令,这里high为14,low为0,表中共有high-low+1个分支项,当jvm遇到tableswitch指令时,它会检测switch(key)中的key值是否在low~high之间,如果不是,直接执行default部分,如果在这个范围之内,它使用key-low这个项指定的地点跳转。可见,tableswitch的效率是非常高的。

例二:

public class Test2 { public static void main(String[] args) { int i = 3; switch (i) { case 3: System.out.println("3"); break; case 20: System.out.println("20"); break; case 50: System.out.println("50"); break; case 100: System.out.println("100"); break; } } }

反汇编代码:

Code: 0: iconst_3 1: istore_1 2: iload_1 3: lookupswitch{ //4 3: 44; 20: 55; 50: 66; 100: 77; default: 85 } 44: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 47: ldc #3; //String 3 49: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 52: goto 85 55: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 58: ldc #5; //String 20 60: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 63: goto 85 66: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 69: ldc #6; //String 50 71: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 74: goto 85 77: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 80: ldc #7; //String 100 82: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 85: return

这里使用的是lookupswitch:

3:lookupswitch{ //4
3: 44;
20: 55;
50: 66;
100: 77;
default: 85 }
这种情况下,必须依次检测每一个项目看是否和switch(key) 中的key匹配,如果遇到匹配的直接跳转,如果遇到比key值大的,执行default,因为3,20,50,100这些项目是按照升序排列的,所以遇到比 key值大的case值后就可以确定后面没有符合条件的值了。另外一点,升序排列也允许jvm实现这条指令时进行优化,比如采用二分搜索的方式取代线性扫描等。

最后,记住jvm规范中的几句话:

  Compilation of switch statements uses the tableswitch and lookupswitch instructions. The tableswitch instruction is used when the cases of the switch can be efficiently represented as indices into a table of target offsets. The default target of the switch is used if the value of the expression of the switch falls outside the range of valid indices.

  Where the cases of the switch are sparse, the table representation of the tableswitch instruction becomes inefficient in terms of space. The lookupswitch instruction may be used instead.

  The Java virtual machine specifies that the table of the lookupswitch instruction must be sorted by key so that implementations may use searches more efficient than a linear scan. Even so, the lookupswitch instruction must search its keys for a match rather than simply perform a bounds check and index into a table like tableswitch. Thus, a tableswitch instruction is probably more efficient than a lookupswitch where space considerations permit a choice.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值