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

Why the switchcase statement in Java works like this?

本问题已经有最佳答案,请猛点这里访问。

为什么Java中的Twitter case语句只使用整数、短、字节和字符,而不是其他数据类型?有什么好处?请详细解释。


通常,语言设计问题归结为"因为设计师决定这样做。"这只是另一个时代。

但是Java有一些起源于C,它做了同样的事情,在80的时候,这个决定向我解释,因为编译器可以把开关转换成跳转表:基本上,每个代码块的地址都放在一个表中,EDCOX1×0个字变成一个范围检查,后面是一个表查找(通常索引成一个数组或一个数组)。t至少链接数组列表)使用传入的值获取地址,然后跳转到该地址。在那种情况下,只有整数才有意义。记住,计算机并不是一直像现在这样快。C是在70年代早期根据60年代后期的工作设计的,当时计算机速度慢得多。

与Java和C相同的一些语法传统,如JavaScript,使EDCOX1 0成为另一种编写EDCOX1×2的方法,并且不限制被检查的类型为整数类型,这可能是因为在90的设计中,这成为一个现实的选择。或者仅仅是因为JavaScript的设计者(BrendanEich)喜欢这样。

下面,Baadshah问:

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

首先,让我们回顾一下int案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  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;
  }

产生这样的字节码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 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来防止碰撞:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  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;
  }

变成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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基元数据类型和枚举。在JDK7中,他们认识到java.lang.string也是一个常量,并且已经被添加到switch语句支持的数据类型列表中。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 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();
 }

}