这个功能是否会被放入以后的Java版本中?
有人可以解释为什么我不能这样做,就像Java的switch
声明的技术方式一样吗?
带有String
案例的switch语句已在Java SE 7中实现,至少在它们首次请求后 16年内实现.没有提供延迟的明确原因,但可能与性能有关.
该功能现已通过javac
"去糖"过程实施; 使用声明中的String
常量的干净,高级语法case
在编译时扩展为模式后面的更复杂的代码.生成的代码使用始终存在的JVM指令.
switch
带有String
案例的A 在编译期间被转换为两个开关.第一个将每个字符串映射到一个唯一的整数 - 它在原始开关中的位置.这是通过首先打开标签的哈希码来完成的.相应的情况是一个if
测试字符串相等性的语句; 如果哈希上有冲突,则测试是级联的if-else-if
.第二个开关镜像原始源代码中的那个,但用它们对应的位置替换案例标签.这个两步过程可以很容易地保留原始开关的流量控制.
有关更多技术深度switch
,您可以参考JVM规范,其中描述了switch语句的编译.简而言之,有两种不同的JVM指令可用于交换机,具体取决于案例使用的常量的稀疏性.两者都依赖于为每种情况使用整数常量来有效执行.
如果常量是密集的,则它们被用作索引(在减去最低值之后)到指令指针表中 - tableswitch
指令.
如果常量是稀疏的,则执行对正确情况的二进制搜索 - lookupswitch
指令.
在switch
对String
对象进行去糖时,可能会使用这两个指令.在lookupswitch
对散列码的第一开关以找到的情况下的原始位置是合适的.由此产生的序数非常适合a tableswitch
.
两个指令都要求在编译时对分配给每个案例的整数常量进行排序.在运行时,虽然O(1)
性能tableswitch
通常看起来好于O(log(n))
性能lookupswitch
,但它需要一些分析来确定表是否足够密集以证明时空权衡.Bill Venners写了一篇很棒的文章,详细介绍了这一点,以及其他Java流程控制指令的内幕.
在JDK 7之前,enum
可以近似String
基于a 的开关.这使用编译器为每种类型生成的静态valueOf
方法enum
.例如:
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 } }
基于整数的开关可以针对非常高效的代码进行优化.基于其他数据类型的开关只能编译为一系列if()语句.
因此,C&C++只允许对整数类型进行切换,因为它对其他类型没有意义.
C#的设计师认为风格很重要,即使没有优势.
Java的设计者显然像C的设计师一样思考.
James Curran简洁地说:"基于整数的开关可以优化为非常有效的代码.基于其他数据类型的开关只能编译为一系列if()语句.因此,C&C++只允许在整数类型上进行切换,因为它与其他类型毫无意义."
我认为,只有这样,一旦你开始转换非原语,你需要开始考虑"等于"与"==".首先比较两个字符串可能是一个相当冗长的过程,增加了上面提到的性能问题.其次,如果有字符串切换,则需要切换字符串忽略大小写,切换字符串考虑/忽略语言环境,根据正则表达式切换字符串....我会赞成一个为节省大量时间的决定语言开发人员花费了少量时间给程序员.
String
也可以显示自1.7以来直接使用的示例:
public static void main(String[] args) { switch (args[0]) { 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; } }
除了上面的好论点之外,我switch
还要补充一点,今天很多人都认为Java过程的过时(过去C次)已经过时了.
我不完全赞同这个观点,我认为switch
在某些情况下它可以有用,至少因为它的速度,而且无论如何它比else if
我在某些代码中看到的一些级联数字更好......
但实际上,值得研究一下你需要一个开关的情况,看看它是否不能被更多的OO取代.例如Java 1.5+中的枚举,也许是HashTable或其他一些集合(有时我很遗憾,我们没有(匿名)函数作为一等公民,如Lua - 没有switch - 或JavaScript)甚至多态.
如果您不使用JDK7或更高版本,则可以使用hashCode()
它来模拟它.因为String.hashCode()
通常为不同的字符串返回不同的值并且总是为相等的字符串返回相等的值,所以它相当可靠(不同的字符串可以产生与注释中提到的@Lii相同的哈希码,例如"FB"
和"Ea"
).请参阅文档.
所以,代码看起来像这样:
String s = ""; switch(s.hashCode()) { case "Hello".hashCode(): break; case "Goodbye".hashCode(): break; }
这样,你在技术上开启了int
.
或者,您可以使用以下代码:
public final class Switch{ private final HashMap cases = new HashMap (0); public void addCase(T object, Runnable action) { this.cases.put(object, action); } public void SWITCH(T object) { for (T t : this.cases.keySet()) { if (object.equals(t)) { // This means that the class works with any object! this.cases.get(t).run(); break; } } } }