为什么Java中的switchcase语句是这样工作的?

这个问题在这里已经有了答案:

  • 为什么我不能打开一个字符串? 14个答案

  • 通常语言设计问题归结为“因为这是设计师决定这样做的方式。” 这只是另一个时代。

    但Java在C语言中有一些起源,他们做了同样的事情,并且在80年代,这个决定被解释为是因为编译器可以将开关转换成跳转表:基本上,每个代码块的地址块都放在表和switch成为范围检查,然后使用您传入的值获取地址,然后跳转到该地址,然后进行表查找(通常索引到数组或至少链接的数组链表)。 在这种情况下只有整数才有意义。 请记住,电脑并不总是像现在这样快。 C是在70年代初期基于60年代后期的工作设计的,当时电脑速度非常慢。

    与Java和C相同的语法传统,如JavaScript,一些语言使得switch只是另一种编写方式, if...else/if...else并且不限制被检查类型为整型,这可能是因为,被设计在90年代,这成为一个现实的选择。 或者也许只是因为JavaScript的设计者(Brendan Eich)更喜欢这种方式。


    在下面,Baadshah问道:

    出于好奇:那么现在它的配套弦如何? 你可以提出一些想法吗?

    首先,让我们回头看看int情况:

      num = Integer.parseInt(args[0]);
      switch (num) {
          case 1:
              System.out.println("You used the special value one");
              break;
          case 42:
              System.out.println("You used the special value forty-two");
              break;
          case 67:
              System.out.println("You used the special value sixty-seven");
              break;
          default:
              System.out.println("You used the a non-special value " + num);
              break;
      }
    

    这样产生这样的字节码:

     19: iload_2       
     20: lookupswitch  { // 3
                    1: 56
                   42: 67
                   67: 78
              default: 89
         }
     56: getstatic     #8   // Field java/lang/System.out:Ljava/io/PrintStream;
     59: ldc           #9   // String You used the special value one
     61: invokevirtual #10  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     64: goto          114
     67: getstatic     #8   // Field java/lang/System.out:Ljava/io/PrintStream;
     70: ldc           #11  // String You used the special value forty-two
     72: invokevirtual #10  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     75: goto          114
     78: getstatic     #8   // Field java/lang/System.out:Ljava/io/PrintStream;
     81: ldc           #12  // String You used the special value sixty-seven
     83: invokevirtual #10  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     86: goto          114
     89: getstatic     #8   // Field java/lang/System.out:Ljava/io/PrintStream;
     92: new           #13  // class java/lang/StringBuilder
     95: dup           
     96: invokespecial #14  // Method java/lang/StringBuilder."":()V
     99: ldc           #15  // String You used the a non-special value 
    101: invokevirtual #16  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    104: iload_2       
    105: invokevirtual #17  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    108: invokevirtual #18  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    111: invokevirtual #10  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

    我们可以看到int在表中的查找。

    那么你怎么用字符串来做到这一点? 那么,一个答案就是将switch转换为if...else if...else结构。 但是他们做了比这更聪明的事情:他们使用散列码进行优化,然后使用equals来防止冲突:

      switch (str) {
          case "abc":
              System.out.println("You used the special value 'abc'");
              break;
          case "def":
              System.out.println("You used the special value 'def'");
              break;
          case "ghi":
              System.out.println("You used the special value 'ghi'");
              break;
          default:
              System.out.println("You used the a non-special value '" + str + "'");
              break;
      }
    

    变为:

    124: aload         4
    126: invokevirtual #19  // Method java/lang/String.hashCode:()I
    129: lookupswitch  { // 3
                96354: 164
                99333: 180
               102312: 196
              default: 209
         }
    164: aload         4
    166: ldc           #20  // String abc
    168: invokevirtual #21  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
    171: ifeq          209
    174: iconst_0      
    175: istore        5
    177: goto          209
    180: aload         4
    182: ldc           #22  // String def
    184: invokevirtual #21  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
    187: ifeq          209
    190: iconst_1      
    191: istore        5
    193: goto          209
    196: aload         4
    198: ldc           #23  // String ghi
    200: invokevirtual #21  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
    203: ifeq          209
    206: iconst_2      
    207: istore        5
    209: iload         5
    211: tableswitch   { // 0 to 2
                    0: 236
                    1: 247
                    2: 258
              default: 269
         }
    236: getstatic     #8   // Field java/lang/System.out:Ljava/io/PrintStream;
    239: ldc           #24  // String You used the special value 'abc'
    241: invokevirtual #10  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    244: goto          299
    247: getstatic     #8   // Field java/lang/System.out:Ljava/io/PrintStream;
    250: ldc           #25  // String You used the special value 'def'
    252: invokevirtual #10  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    255: goto          299
    258: getstatic     #8   // Field java/lang/System.out:Ljava/io/PrintStream;
    261: ldc           #26  // String You used the special value 'ghi'
    263: invokevirtual #10  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    266: goto          299
    269: getstatic     #8   // Field java/lang/System.out:Ljava/io/PrintStream;
    272: new           #13  // class java/lang/StringBuilder
    275: dup           
    276: invokespecial #14  // Method java/lang/StringBuilder."":()V
    279: ldc           #27  // String You used the a non-special value '
    281: invokevirtual #16  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    284: aload_3       
    285: invokevirtual #16  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    288: ldc           #28  // String '
    290: invokevirtual #16  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    293: invokevirtual #18  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    296: invokevirtual #10  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

    看看他们在那里做了什么? 现在基本上有两个switches :一个是根据散列码为每个案例获得一个唯一的编号(但是使用equals双重检查),然后是第二个派发。


    JDK6 switch语句处理char,byte,int原始数据类型和枚举。 在JDK 7中,他们意识到java.lang.String也是一个常量,并已添加到switch语句支持的数据类型列表中。

    例如,以下代码在JDK7中正常工作。

     public static void OpenSource(String language) 
    {
     switch (language) {
     case "PERL":
       System.out.println("PERL");
       break;
     case "Python":
       System.out.println("Python");
       break;
     case "Ruby":
       System.out.println("Ruby");
       break;
     case "PHP":
       System.out.println("PHP");
       break;
      default:
       throw new IllegalArgumentException();
     }
    

    }

    链接地址: http://www.djcxy.com/p/84507.html

    上一篇: Why the switchcase statement in Java works like this?

    下一篇: String as switch statement