当前位置:  开发笔记 > 编程语言 > 正文

为什么我不能在String上使用switch语句?

如何解决《为什么我不能在String上使用switch语句?》经验,为你挑选了8个好方法。

这个功能是否会被放入以后的Java版本中?

有人可以解释为什么我不能这样做,就像Java的switch声明的技术方式一样吗?



1> erickson..:

带有String案例的switch语句已在Java SE 7中实现,至少在它们首次请求后 16年内实现.没有提供延迟的明确原因,但可能与性能有关.

在JDK 7中实现

该功能现已通过javac "去糖"过程实施; 使用声明中的String常量的干净,高级语法case在编译时扩展为模式后面的更复杂的代码.生成的代码使用始终存在的JVM指令.

switch带有String案例的A 在编译期间被转换为两个开关.第一个将每个字符串映射到一个唯一的整数 - 它在原始开关中的位置.这是通过首先打开标签的哈希码来完成的.相应的情况是一个if测试字符串相等性的语句; 如果哈希上有冲突,则测试是级联的if-else-if.第二个开关镜像原始源代码中的那个,但用它们对应的位置替换案例标签.这个两步过程可以很容易地保留原始开关的流量控制.

在JVM中切换

有关更多技术深度switch,您可以参考JVM规范,其中描述了switch语句的编译.简而言之,有两种不同的JVM指令可用于交换机,具体取决于案例使用的常量的稀疏性.两者都依赖于为每种情况使用整数常量来有效执行.

如果常量是密集的,则它们被用作索引(在减去最低值之后)到指令指针表中 - tableswitch指令.

如果常量是稀疏的,则执行对正确情况的二进制搜索 - lookupswitch指令.

switchString对象进行去糖时,可能会使用这两个指令.在lookupswitch对散列码的第一开关以找到的情况下的原始位置是合适的.由此产生的序数非常适合a tableswitch.

两个指令都要求在编译时对分配给每个案例的整数常量进行排序.在运行时,虽然O(1)性能tableswitch通常看起来好于O(log(n))性能lookupswitch,但它需要一些分析来确定表是否足够密集以证明时空权衡.Bill Venners写了一篇很棒的文章,详细介绍了这一点,以及其他Java流程控制指令的内幕.

在JDK 7之前

在JDK 7之前,enum可以近似String基于a 的开关.这使用编译器为每种类型生成的静态valueOf方法enum.例如:

Pill p = Pill.valueOf(str);
switch(p) {
  case RED:  pop();  break;
  case BLUE: push(); break;
}


if-elseif-elseif-elseif-else可能会更快,但我会从100个中删除更清晰的代码99次.字符串是不可变的,缓存它们的哈希代码,因此"计算"哈希很快.人们必须分析代码以确定有什么好处.
使用If-Else-If而不是基于字符串的交换的哈希可能更快.我发现只存储一些物品时字典相当昂贵.
添加switch(String)的原因是它不符合switch()语句所期望的性能保证.他们不想"误导"开发者.坦率地说,我不认为他们应该保证switch()的性能开始.

2> JeeBee..:

如果您在代码中有一个可以打开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;
}


有关此方法的详细说明,请参见http://www.xefer.com/2006/12/switchonstring.
这种技术还可以让您决定不区分大小写,别名等问题.而不是依赖语言设计师来提出"一刀切"的解决方案.

3> Thulani Chiv..:

以下是基于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
  }
}



4> James Curran..:

基于整数的开关可以针对非常高效的代码进行优化.基于其他数据类型的开关只能编译为一系列if()语句.

因此,C&C++只允许对整数类型进行切换,因为它对其他类型没有意义.

C#的设计师认为风格很重要,即使没有优势.

Java的设计者显然像C的设计师一样思考.


可以使用哈希表非常有效地实现基于任何可哈希对象的开关 - 请参阅.NET.所以你的理由并不完全正确.
@Nalandial:实际上,通过编译器的一点努力,它并不昂贵,因为当知道字符串集时,生成完美的散列非常容易(但这不是由.NET完成的;也许不值得努力.
@Nalandial和@Konrad Rudolph - 虽然哈希一个字符串(由于它是不可变的性质)似乎是这个问题的解决方案,你必须记住所有非最终对象都可以覆盖它们的哈希函数.这使得在编译时难以确保交换机的一致性.

5> DJClayworth..:

James Curran简洁地说:"基于整数的开关可以优化为非常有效的代码.基于其他数据类型的开关只能编译为一系列if()语句.因此,C&C++只允许在整数类型上进行切换,因为它与其他类型毫无意义."

我认为,只有这样,一旦你开始转换非原语,你需要开始考虑"等于"与"==".首先比较两个字符串可能是一个相当冗长的过程,增加了上面提到的性能问题.其次,如果有字符串切换,则需要切换字符串忽略大小写,切换字符串考虑/忽略语言环境,根据正则表达式切换字符串....我会赞成一个为节省大量时间的决定语言开发人员花费了少量时间给程序员.



6> Gunnar Forsg..:

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;
    }

}



7> PhiLho..:

除了上面的好论点之外,我switch还要补充一点,今天很多人都认为Java过程的过时(过去C次)已经过时了.

我不完全赞同这个观点,我认为switch在某些情况下它可以有用,至少因为它的速度,而且无论如何它比else if我在某些代码中看到的一些级联数字更好......

但实际上,值得研究一下你需要一个开关的情况,看看它是否不能被更多的OO取代.例如Java 1.5+中的枚举,也许是HashTable或其他一些集合(有时我很遗憾,我们没有(匿名)函数作为一等公民,如Lua - 没有switch - 或JavaScript)甚至多态.



8> HyperNeutrin..:

如果您不使用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;
            }
        }
    }
}


产生与注释中提到的@Lii相同的哈希码,例如
两个不同的字符串可以具有相同的哈希码,因此如果您打开哈希码,可能会采取错误的案例分支.
推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有