关于swith()中表达式的知识
2014-11-20 13:37
120 查看
switch是控制选择的一种方式,编译器生成代码时可以对这种结构进行特定的优化,从而产生效率比较高的代码。在java中,编译器根据分支的情况,分别产生tableswitch,lookupswitch两中情况,其中tableswitch适用于分支比较集中的情况,而lookupswitch适用与分支比较稀疏的情况。不过怎么算稀疏,怎么算集中就是编译器的决策问题了,这里不做深入的分析。
简单的找几个例子。
例一:
Java代码
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;
}
}
}
反汇编代码可以发现其跳转表的结构:
Java代码
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的效率是非常高的。
例二:
Java代码
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;
}
}
}
反汇编代码:
Java代码
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
be efficiently represented as indices into a table of target offsets. The
used if the value of the expression of the
Where the cases of the
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.
简单的找几个例子。
例一:
Java代码
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;
}
}
}
反汇编代码可以发现其跳转表的结构:
Java代码
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的效率是非常高的。
例二:
Java代码
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;
}
}
}
反汇编代码:
Java代码
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
switchstatements uses the tableswitch and lookupswitch instructions. The tableswitch instruction is used when the cases of the
switchcan
be efficiently represented as indices into a table of target offsets. The
defaulttarget of the
switchis
used if the value of the expression of the
switchfalls outside the range of valid indices.
Where the cases of the
switchare 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.
相关文章推荐
- 关于正则表达式,你应该会用到的一些知识
- 【脚本语言系列】关于Python基础知识Lambda表达式,你需要知道的事
- JS中关于正则表达式的基本知识
- 关于python的基础知识11--正则表达式
- java 关于正则表达式的基本知识
- 关于正则表达式的一些知识和函数总结
- 关于Java编程的一些小知识
- 正则表达式基础知识 (转)
- 转载:关于linux图形界面的基本知识
- GeoGraphics文档中关于坐标系统的一些知识
- 一段关于正则表达式的例子
- 关于 SQL 和 Recordset 的小知识
- 关于jxl的一点知识
- 关于java的正则表达式
- 又长知识了~~关于DDR内存的经历!
- 关于句柄的基础知识
- 关于邮件地址的正则表达式
- 关于堆和栈的一些知识(Z)
- 关于知识管理系统和信息管理系统的区别
- 关于 SQL 和 Recordset 的小知识