Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0fa06f8

Browse filesBrowse files
committed
优化“为什么不能用string类型进行switch判断”
1 parent 05aceb8 commit 0fa06f8
Copy full SHA for 0fa06f8

File tree

Expand file treeCollapse file tree

2 files changed

+68
-15
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+68
-15
lines changed

‎README.md

Copy file name to clipboardExpand all lines: README.md
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ stackoverflow-Java-top-qa
3434
* [如何将String转换为enum](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/convert-a-string-to-an-enum-in-java.md)
3535
* [在Java中声明数组](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/Declare-array-in-Java.md)
3636
* [反射是什么及其用途](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/What-is-reflection-and-why-is-it-useful.md.md)
37-
37+
* [为什么不能用string类型进行switch判断](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/Why-can't-I-switch-on-a-String.md)
3838

3939
> 编程技巧
4040

‎contents/Why-can't-I-switch-on-a-String.md

Copy file name to clipboardExpand all lines: contents/Why-can't-I-switch-on-a-String.md
+67-14Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,81 @@
1-
# Why can't I switch on a String
1+
# 为什么不能用string类型进行switch判断
22

33
##问题描述
44
为什么不能用string类型进行switch判断?
5-
是否作为一个功能将会被添加如java后续版本中
6-
有人能给我一篇文章,或者解释一下为什么不能这样做,说明java中switch语句的运行方式
5+
在java的后续版本中,是否会增加这个新特性
6+
有人能给我一篇文章,解释一下为什么不能这样做,或者进一步说明java中switch语句的运行方式
77

88
##回答
9-
用string作为case来使用switch语句已经在java SE7中被实现了,距离这个问题被提出至少有16年了。一个明确的原因迟迟未提供,但是性能要求它必须这样做
9+
在switch语句中用string作为case,这个特性已经在java SE7 中被实现了,距离 [这个'bug'](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179) 被提出至少也有16年了。为何迟迟不提供这个特性,原因不明。但可以推测,可能跟性能有关
1010

1111
**Implementtation in JDK 7**
12-
这个特性现在已经被实现,`javac`with a ["de-sugaring peocess"](http://blogs.oracle.com/darcy/entry/project_coin_string_switch_anatomy)。一个高级的,清楚的使用String常量的语法能在case声明中被使用,会在编译时扩展成为更复杂的代码模式,生成的代码使用jvm指令也一直存在。
13-
switch将string用作case在编译时翻译成两个switch。第一次每一个string映射到一个独一无二的整数,它的位置在原始的switch上。这是通过在case上的hashcode进行switch选择,相应的案例是一个if语句,测试字符串是否相等。如果有哈希碰撞,测试就像`if-else-if`
14-
第二次switch反映在最初的源代码中,用相应位置上的整数代替响应的string标签case。这两步过程更容易保留原始switch的流程控制。
12+
13+
在JDK7中,这个特性已经实现了。在编译阶段,以string作为case值的代码,会按照特定的模式,被转换为更加复杂的代码。最终的执行代码将是一些使用了JVM指令的代码。
14+
15+
究竟是如何转换的呢?我们直接看看源码及编译后的代码。源代码:
16+
```
17+
public class StringInSwitchCase {
18+
public static void main(String[] args) {
19+
String mode = args[0];
20+
switch (mode) {
21+
case "ACTIVE":
22+
System.out.println("Application is running on Active mode");
23+
break;
24+
case "PASSIVE":
25+
System.out.println("Application is running on Passive mode");
26+
break;
27+
case "SAFE":
28+
System.out.println("Application is running on Safe mode");
29+
}
30+
}
31+
}
32+
```
33+
编译后再反编译的代码:
34+
```
35+
import java.io.PrintStream;
36+
37+
public class StringInSwitchCase{
38+
public StringInSwitchCase() { }
39+
40+
public static void main(string args[]) {
41+
String mode = args[0];
42+
String s; switch ((s = mode).hashCode()) {
43+
default: break;
44+
case -74056953:
45+
if (s.equals("PASSIVE")) {
46+
System.out.println("Application is running on Passive mode");
47+
}
48+
break;
49+
case 2537357:
50+
if (s.equals("SAFE")) {
51+
System.out.println("Application is running on Safe mode");
52+
}
53+
break;
54+
case 1925346054:
55+
if (s.equals("ACTIVE")) {
56+
System.out.println("Application is running on Active mode");
57+
}
58+
break;
59+
}
60+
}
61+
}
62+
```
63+
64+
包含case string的 switch 语句,在编译时会转为为嵌套代码(switch+if)。第一个switch将 case 中的string转为唯一的integer值。这个integer值就是原先string的hashcode值。在case的逻辑中,会加入if语句,这个if语句用于进一步检查string值是否跟原先的case string匹配。这样可以防止hash碰撞,确保代码的健壮。这本质上是一种语法糖,既支持了string作为case值这一特性,又能确保逻辑正确性。
1565

1666
**Switchs in the JVM**
17-
在switch的更多深层技术上,可以参考JVM规范,[compliation of switch statements](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10)有响应的描述。简单概括说,根据使用的常量的多少,switch有两种不同的JVM指令来执行,每种情况下都取决于使用整数常量来有效的执行。
1867

19-
如果常量很多,case会在一个指令表被用作索引(减去最小值后)——`tablewitch`指令
20-
如果常量相对较少,那么可用二分查找来找到正确的case--`lookupswitch`指令
68+
switch的更多深层技术实现,可以参考JVM规范,[compliation of switch statements](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10)。简单概括说,根据使用的常量的多寡,switch会对应到两种不同的JVM指令。JVM指令有所不同,归根结底都是为了代码的效率。
2169

22-
`de-sugaring`中,一个switch(String)的对象,两种指令都会被用到。`lookupswitch`指令适用于第一次计算哈希值找到初始位置,由此产生的顺序表很自然的用`tableswitch`指令
70+
如果常量很多,会将case的int值去掉最低位后作为索引,放到一个指针表中——也就是所谓的`tablewitch`指令
2371

24-
两种指令在编译时都需要将整数常量赋值给每个case。在运行时,虽然`tableswitch`O(1)的性能通常要好于`lookupswitch`O(log(n))的性能,但是前者需要一定的分析来决定是否有足够的case来满足时间空间的平衡。Bill Venners的文章[a great article](http://www.artima.com/underthehood/flowP.html)在细节上讲述的更多,以及涉及到从底层的出发,来看其他java流程控制指令。
72+
如果常量相对较少,那么可用二分查找来找到正确的case--也就是所谓的`lookupswitch`指令
73+
74+
这两种指令,都要求在编译时确保case的对应值是integer常量。在运行时,虽然`tableswitch`O(1)的性能通常要好于`lookupswitch`O(log(n))的性能。但是前者需要更多的空间开销,因此需要兼顾空间及时间综合考虑性价比。Bill Venners的文章[a great article](http://www.artima.com/underthehood/flowP.html)有更多深入的分析。
2575

2676
**Before JDK 7**
27-
在JDK之前,枚举近似的用String作为switch的case,这使用了静态方法`valueOf`,编译器生成了一个枚举类型。例子如下:
77+
78+
在JDK之前,可以用枚举来实现类似的需求。它和在case中使用string有异曲同工之妙。例如如下:
2879
```
2980
Pill p = Pill.valueOf(str);
3081
switch(p) {
@@ -33,7 +84,9 @@ switch(p) {
3384
}
3485
```
3586

36-
[阅读原文:Why can`t I switch on a String](http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string)
87+
[stackoverflow原链接:Why can`t I switch on a String](http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string)
88+
89+
[可参考中文文章《Java中字符串switch的实现细节》](http://www.deepinmind.com/java/2014/05/08/how-string-in-switch-works-in-java-7.html)
3790

3891

3992

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.