简单的找几个例子。
例一:
- 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.