为什么不能在字符串上使用switch语句?

此功能是否将在以后的Java版本中使用?

有人可以解释为什么我不能这样做吗,例如Java的switch语句的技术方式?


#1楼

Groovy轻而易举; 我嵌入了groovy jar并创建了一个groovy实用程序类来完成所有这些事情,而我发现这些事情在Java中令人生厌(因为我在企业中一直使用Java 6)。

it.'p'.each{
switch (it.@name.text()){
   case "choclate":
     myholder.myval=(it.text());
     break;
     }}...

#2楼

多年来,我们一直在使用(n个开源)预处理器。

//#switch(target)
case "foo": code;
//#end

预处理后的文件名为Foo.jpp,并使用ant脚本处理为Foo.java。

优点是将其处理为可在1.0上运行的Java(尽管通常我们仅支持1.4)。 与使用枚举或其他变通方法进行伪造相比,执行此操作(许多字符串切换)要容易得多-代码更易于阅读,维护和理解。 IIRC(目前无法提供统计信息或技术推理)也比自然的Java等效更快。

缺点是您不编辑Java,所以它的工作流程(编辑,处理,编译/测试)要多一些,而且IDE会链接回Java,这有点麻烦(开关变成一系列if / else逻辑步骤)并且不保持开关箱顺序。

我不建议在1.7+上使用它,但是如果您要编写针对较早JVM的Java(因为Joe public很少安装最新的JVM),它就很有用。

您可以从SVN获得它也可以在线浏览代码 。 您将需要EBuild才能按原样构建它。


#3楼

从1.7开始,也可以直接使用String的示例:

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

}

#4楼

如果您没有使用JDK7或更高版本,则可以使用hashCode()进行仿真。 produce the same hash code as @Lii mentioned in a comment, such as "FB" and "Ea" ) See documentation . 由于String.hashCode()通常会为不同的字符串返回不同的值,并且始终为相等的字符串返回相等的值,因此这是相当可靠的(不同的字符串生成与注释中提到的@Lii相同的哈希码,例如"FB""Ea" )参见文档

因此,代码如下所示:

String s = "<Your String>";

switch(s.hashCode()) {
case "Hello".hashCode(): break;
case "Goodbye".hashCode(): break;
}

这样,您就可以从技术上打开int

另外,您可以使用以下代码:

public final class Switch<T> {
    private final HashMap<T, Runnable> cases = new HashMap<T, Runnable>(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;
            }
        }
    }
}

#5楼

public class StringSwitchCase { 

    public static void main(String args[]) {

        visitIsland("Santorini"); 
        visitIsland("Crete"); 
        visitIsland("Paros"); 

    } 

    public static void visitIsland(String island) {
         switch(island) {
          case "Corfu": 
               System.out.println("User wants to visit Corfu");
               break; 
          case "Crete": 
               System.out.println("User wants to visit Crete");
               break; 
          case "Santorini": 
               System.out.println("User wants to visit Santorini");
               break; 
          case "Mykonos": 
               System.out.println("User wants to visit Mykonos");
               break; 
         default: 
               System.out.println("Unknown Island");
               break; 
         } 
    } 

} 

#6楼

带有String switch语句已在Java SE 7中实现 ,至少在首次提出要求后的 16年 没有提供延迟的明确原因,但可能与性能有关。

在JDK 7中实现

现在,该功能已通过javac 中的“反糖化”过程实现。case声明中使用String常量的一种干净的高级语法在编译时扩展为遵循模式的更复杂的代码。 生成的代码使用始终存在的JVM指令。

在编译过程中,具有Stringswitch将转换为两个开关。 第一个将每个字符串映射到一个唯一的整数-它在原始开关中的位置。 这是通过首先打开标签的哈希码来完成的。 相应的情况是一个if语句,用于测试字符串是否相等; 如果哈希上有冲突,则测试为级联的if-else-if 。 第二个开关在原始源代码中进行镜像,但是用相应的位置替换了大小写标签。 此两步过程使保留原始交换机的流量控制变得容易。

在JVM中切换

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

如果常量是密集的,则将它们用作指令指针表( tableswitch指令)的索引(减去最小值后)。

如果常量稀疏,则执行二进制搜索以lookupswitch正确的大小写lookupswitch指令。

在对String对象的switch进行除糖处理时,很可能会使用这两个指令。 lookupswitch适用于哈希码的第一个开关,以查找案例的原始位置。 产生的序数自然适用于tableswitch

两条指令都要求在编译时对分配给每种情况的整数常量进行排序。 在运行时,而O(1)性能tableswitch一般都比较好显得比O(log(n))的性能lookupswitch ,它需要一些分析,以确定该表是否是密集足以证明时空权衡。 Bill Venners写了一篇很棒的文章 ,其中更详细地介绍了此内容,同时还深入介绍了其他Java流程控制指令。

在JDK 7之前

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

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

#7楼

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

因此,C&C ++仅允许在整数类型上进行切换,因为它与其他类型无关。

C#的设计师认为即使没有优势,样式也很重要。

Java的设计师显然像C的设计师那样思考。


#8楼

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

#9楼

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

我的观点(仅此而已)是,一旦您开始启用非基本体,就需要开始考虑“等于”与“ ==”。 首先,比较两个字符串可能是一个相当漫长的过程,从而增加了上面提到的性能问题。 其次,如果需要打开字符串,则需要忽略大小写的字符串,考虑/忽略语言环境的字符串,基于正则表达式的字符串...。我赞成这样的决定,即该决定节省了很多时间。语言开发人员会以少量的时间为程序员付出代价。


#10楼

除了上述良好的论据外,我还要补充一点,今天很多人都将switch视为Java程序过时的剩余部分(回到C时代)。

我没有完全同意这种观点,我认为switch在某些情况下可能会有用,至少是因为它的速度,而且无论如何, else if我在某些代码中看到的else if ,它比一系列级联的数字要好。

但是确实,值得一看的是您需要一个开关的情况,看看是否不能用更多的OO代替它。 例如Java 1.5+中的枚举,也许是HashTable或其他一些集合(有时我很遗憾,我们没有作为一流公民的(匿名)函数,如Lua(没有开关)或JavaScript)或什至是多态。


#11楼

当您使用intellij时,还请注意:

文件->项目结构->项目

文件->项目结构->模块

如果有多个模块,请确保在“模块”选项卡中设置正确的语言级别。


#12楼

其他答案表明这是在Java 7中添加的,并提供了较早版本的解决方法。 这个答案试图回答“为什么”

Java是对C ++过于复杂的反应。 它被设计为一种简单的简洁语言。

String在语言中有一些特殊情况处理,但对我来说似乎很清楚,设计师正在尝试将特殊大小写和语法糖的数量保持在最低水平。

由于字符串不是简单的原始类型,因此打开字符串非常复杂。 在设计Java时,这不是一个常见的功能,因此与极简设计并不十分吻合。 尤其是因为他们决定对字符串不使用特殊情况==,对于==不适用的情况下的大小写工作会有些奇怪。

在1.0和1.4之间,语言本身几乎保持不变。 Java的大多数增强功能都在库方面。

Java 5改变了一切,该语言得到了实质性扩展。 在版本7和8中进行了进一步的扩展。我希望这种态度的改变是由C#的崛起驱动的


#13楼

不是很漂亮,但这是Java 6和波纹管的另一种方式:

String runFct = 
        queryType.equals("eq") ? "method1":
        queryType.equals("L_L")? "method2":
        queryType.equals("L_R")? "method3":
        queryType.equals("L_LR")? "method4":
            "method5";
Method m = this.getClass().getMethod(runFct);
m.invoke(this);

#14楼

以下是基于JeeBee的帖子的完整示例,使用Java枚举而不是自定义方法。

请注意,在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
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值