Why the switchcase statement in Java works like this?

This question already has an answer here:

  • Why can't I switch on a String? 14 answers

  • Usually language design questions boil down to "because that's the way the designers decided to do it." This is just another of those times.

    But Java has some origins in C, which did the same thing, and in the 80's that decision was explained to me as being because then the compiler could turn the switch into a jump table: Basically, each block of code's address is put in a table and the switch becomes a range check followed by a table lookup (usually indexing into an array or at least linked list of arrays) using the value you pass in to get the address, and then jump to that address. Only integers make sense in that scenario. Remember that computers weren't always as fast as they are now. C was designed in the early 70's based on work in the late 60's, when computers were much slower.

    Some languages in the same syntactic tradition as Java and C, such as JavaScript, make the switch just another way of writing if...else/if...else and don't limit the type being checked to integral types, perhaps because, being designed in the 90's, that became a realistic option. Or perhaps just because the designer of JavaScript (Brendan Eich) preferred it that way.


    Below, Baadshah asks:

    Out of curiosity : Then now how its supporting Strings ??? can you please give some sort of idea ?

    First, let's step back and look at the int case:

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

    That produces bytecode like this:

     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

    We can see the table lookup on the int in action.

    So how do you do that with strings? Well, one answer would be to just turn the switch into an if...else if...else structure. But they did something more clever than that: They used the hashcode to optimize, and then used equals to protect against collisions:

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

    becomes:

    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

    See what they did there? It's basically two switches now: One to get a unique number for each case based on the hashcode (but double-checking with equals ), and then the second to dispatch.


    JDK6 switch statement worked on char, byte, int primitive data types and enum. In JDK 7 they realised that java.lang.String is also a constant and has been added to the list of data types supported by a switch statement.

    For example the following code works fine in 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/84508.html

    上一篇: 在Java1.6中用字符串切换

    下一篇: 为什么Java中的switchcase语句是这样工作的?