switch-case对String的特殊处理
LloydFinch 9/26/2021 JavaJVM
我们在swtich-case的实现原理与优化方案 (opens new window)中说到,switch-case对String的处理是通过String的hashcode来实现的。
那么,如果两个String的hashcode相同会怎么样呢?比如"Aa"和"BB",他们的hashcode都是2112,那么如下函数:
public void test(String a) {
int b = 10;
switch(a) {
case "Aa":
b = 1;
break;
case "BB":
b = 100;
break;
default:
b = 1000;
break;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
是不是传入"Aa"和"BB"都一样呢?不会!
因为如果switch的状态值是String的时候,除了进行hashcode校验,还会使用equals进行值校验,类似于HashMap中的定位逻辑,等价代码如下:
public void test(String a) {
int b = 10;
switch(a.hashCode()) { // 这里使用hashcode进行检索
case 2112: // 转换为hashcode进行检索
if(a.equals("Aa")) { // hashcode命中了,就是用equals来进行二次定位
b = 1;
}else if(a.equals("BB")) {
b = 100;
}
break;
default:
b = 1000;
break;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
可以看到,switch-case在条件值是String的时候,会使用hashcode作为switch-case的状态值,当hashcode相同的时候,再使用equals来进行二次比较,只有hashcode和equals都相同才说明复合条件,这属于分页思想:粗略定位到精准定位。
Tips:并不是只有hashcode相同才使用equals比较,而是不管hashcode是否相同,都会使用equals进行比较。
我们将上述代码编译下,来看字节码:
public void test(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=5, args_size=2
0: bipush 10
2: istore_2
3: aload_1
4: astore_3
5: iconst_m1
6: istore 4
8: aload_3
9: invokevirtual #7 // Method java/lang/String.hashCode:()I // 这里调用了String的hashCode()
12: lookupswitch { // 1
2112: 32 // 只有一个2112,说明合并了,32表示执行第32行字节码
default: 59 // 否则就执行第59行字节码
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
我们接着来看字节码指令(只看注释部分即可),对字节码指令不熟悉可以看这里 (opens new window):
32: aload_3
33: ldc #13 // String BB // 将BB推送至栈顶
35: invokevirtual #15 // Method java/lang/String.equals:(Ljava/lang/Object;)Z // 执行equals语句
38: ifeq 47 // 如果相同(if equals)
41: iconst_1
42: istore 4
44: goto 59
47: aload_3
48: ldc #19 // String Aa // jiangAa推送值栈顶
50: invokevirtual #15 // Method java/lang/String.equals:(Ljava/lang/Object;)Z // 执行equals语句
53: ifeq 59 // 如果相同
56: iconst_0
57: istore 4
59: iload 4
61: lookupswitch { // 2
0: 88
1: 93
default: 99
}
88: iconst_1
89: istore_2
90: goto 103
93: bipush 100
95: istore_2
96: goto 103
99: sipush 1000
102: istore_2
103: return
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
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
上述代码验证了我们的结论: switch-case条件是String时候,使用String的hashCode来作为状态值,如果相同就合并,内部使用equals进行二次比较。