您的位置:首页 > 其它

关于swith()中表达式的知识

2014-11-20 13:37 99 查看
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
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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: