具有String
案例的 Switch 语句已在Java SE 7 中实现 ,至少在首次请求后 16 年内实现。没有提供延迟的明确原因,但可能与性能有关。
该功能现已在javac
实现, 具有 “去糖” 过程;一个干净的高级语法,使用String
常量, case
声明在编译时扩展为模式后面的更复杂的代码。生成的代码使用始终存在的 JVM 指令。
在编译期间,具有String
情况的switch
被转换为两个开关。第一个将每个字符串映射到一个唯一的整数 - 它在原始开关中的位置。这是通过首先打开标签的哈希码来完成的。相应的情况是测试字符串相等性的if
语句; 如果哈希上有冲突,则测试是级联if-else-if
。第二个开关镜像原始源代码中的那个,但用它们对应的位置替换案例标签。这个两步过程可以很容易地保留原始开关的流量控制。
有关switch
更多技术深度,可以参考 JVM 规范,其中描述了 switch 语句的编译 。简而言之,有两种不同的 JVM 指令可用于交换机,具体取决于案例使用的常量的稀疏性。两者都依赖于为每种情况使用整数常量来有效执行。
如果常量是密集的,则它们被用作索引(在减去最低值之后)到指令指针表中 - tableswitch
指令。
如果常量是稀疏的,则执行二进制搜索正确的情况 - lookupswitch
指令。
在脱糖一个switch
上String
对象,两者的指令有可能被使用。 lookupswitch
适用于第一次切换哈希码以查找案例的原始位置。由此产生的序数非常适合tableswitch
。
两个指令都要求在编译时对分配给每个案例的整数常量进行排序。在运行时,而O(1)
性能tableswitch
普遍显得比更好的O(log(n))
性能lookupswitch
,它需要一些分析,以确定该表是否足够密集证明时空权衡。 Bill Venners 写了一篇很棒的文章 ,详细介绍了这一点,以及其他 Java 流程控制指令的内幕。
在 JDK 7 之前, enum
可以近似基于String
的开关。这使用编译器为每个enum
类型生成的静态valueOf
方法。例如:
Pill p = Pill.valueOf(str);
switch(p) {
case RED: pop(); break;
case BLUE: push(); break;
}
如果您在代码中有一个可以打开 String 的位置,那么最好将 String 重构为可能值的枚举,您可以打开它。当然,您可以将字符串的潜在值限制为枚举中的字符串,这可能是也可能不是。
当然,你的枚举可能有一个'other' 条目和一个 fromString(String)方法,那么你可以拥有
ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
case MILK: lap(); break;
case WATER: sip(); break;
case BEER: quaff(); break;
case OTHER:
default: dance(); break;
}
以下是基于 JeeBee 帖子的完整示例,使用 java enum 而不是使用自定义方法。
请注意,在 Java SE 7 及更高版本中,您可以在 switch 语句的表达式中使用 String 对象。
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String current = args[0];
Days currentDay = Days.valueOf(current.toUpperCase());
switch (currentDay) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
System.out.println("boring");
break;
case THURSDAY:
System.out.println("getting better");
case FRIDAY:
case SATURDAY:
case SUNDAY:
System.out.println("much better");
break;
}
}
public enum Days {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
}