gpt4 book ai didi

java - String 是关于 switch 的数字类型并且总是编译为 lookupswitch 吗?

转载 作者:搜寻专家 更新时间:2023-11-01 03:16:07 26 4
gpt4 key购买 nike

以下代码返回给定的 String s 是否等于任何其他硬编码字符串。该方法使用 switch 语句来执行此操作:

public class SwitchOnString {
public static boolean equalsAny(String s) {
switch (s) {
case "string 1":
return true;
case "string 2":
return true;
default:
return false;
}
}
}

根据 Java 虚拟机规范 (JMS) 3.10 Compiling Switches "

Compilation of switch statements uses the tableswitch and lookupswitch instructions.

此外

tableswitch and lookupswitch instructions operate only on int data.

我阅读了第 3.10 章,但没有找到提到的任何地方 String

唯一一个间接接近的句子是:

Other numeric types must be narrowed to type int for use in a switch.

问题一:
此上下文中的 String 也是数字类型吗?还是我错过了什么?

SwitchOnString 上的 javap -c 显示:

Compiled from "SwitchOnString.java"
public class playground.SwitchOnString {
public playground.SwitchOnString();
...

public static boolean equalsAny(java.lang.String);
Code:
0: aload_0
1: dup
2: astore_1
3: invokevirtual #16 // Method java/lang/String.hashCode:()I
6: lookupswitch { // 2
1117855161: 32
1117855162: 44
default: 60
}
...

}

显然,hashCode 值用作 caseint 键。这可能匹配:

The lookupswitch instruction pairs int keys (the values of the case labels) ...

继续 tableswitchlookupswitch JMS 说:

The tableswitch instruction is used when the cases of the switch can be efficiently represented as indices into a table of target offsets. (...) Where the cases of the switch are sparse, the table representation of the tableswitch instruction becomes inefficient in terms of space. The lookupswitch instruction may be used instead.

如果我做对了,那么情况越稀疏,lookupswitch 就越有可能被使用。

问题二:
但是看看字节码:
两个字符串大小写是否足够稀疏以将 switch 编译为 lookupswitch?或者 String 上的每个开关都将编译为 lookupswitch 吗?

最佳答案

规范没有说明如何编译 switch 语句,这取决于编译器。

在这方面,JVMS 声明“其他数字类型必须缩小为 int 类型以用于 switch”并没有说 Java 编程语言将做这样的转换,也不是 StringEnum 是数字类型。 IE。 longfloatdouble 是数字类型,但不支持将它们与 switch< 一起使用 Java 编程语言中的语句。

所以 language 规范说支持 switch over String ,因此,编译器必须找到一种方法将它们编译为字节码。使用哈希码等不变属性是一种常见的解决方案,但原则上,也可以使用长度或任意字符等其他属性。

如“Why switch on String compiles into two switches ”和“Java 7 String switch decompiled: unexpected instruction ", javac 当前在编译 switch over String 值时在字节码级别生成两条 switch 指令(ECJ 也生成两条指令,但细节可能不同).

然后,编译器必须选择一个 lookupswitchtableswitch操作说明。 javac 在数字不稀疏时确实使用 tableswitch,但前提是语句有两个以上的 case 标签。

所以当我编译如下方法时:

public static char two(String s) {
switch(s) {
case "a": return 'a';
case "b": return 'b';
}
return 0;
}

我明白了

public static char two(java.lang.String);
Code:
0: aload_0
1: astore_1
2: iconst_m1
3: istore_2
4: aload_1
5: invokevirtual #9 // Method java/lang/String.hashCode:()I
8: lookupswitch { // 2
97: 36
98: 50
default: 61
}
36: aload_1
37: ldc #10 // String a
39: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 61
45: iconst_0
46: istore_2
47: goto 61
50: aload_1
51: ldc #12 // String b
53: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 61
59: iconst_1
60: istore_2
61: iload_2
62: lookupswitch { // 2
0: 88
1: 91
default: 94
}
88: bipush 97
90: ireturn
91: bipush 98
93: ireturn
94: iconst_0
95: ireturn

但是当我编译的时候,

public static char three(String s) {
switch(s) {
case "a": return 'a';
case "b": return 'b';
case "c": return 'c';
}
return 0;
}

我明白了

public static char three(java.lang.String);
Code:
0: aload_0
1: astore_1
2: iconst_m1
3: istore_2
4: aload_1
5: invokevirtual #9 // Method java/lang/String.hashCode:()I
8: tableswitch { // 97 to 99
97: 36
98: 50
99: 64
default: 75
}
36: aload_1
37: ldc #10 // String a
39: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 75
45: iconst_0
46: istore_2
47: goto 75
50: aload_1
51: ldc #12 // String b
53: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 75
59: iconst_1
60: istore_2
61: goto 75
64: aload_1
65: ldc #13 // String c
67: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
70: ifeq 75
73: iconst_2
74: istore_2
75: iload_2
76: tableswitch { // 0 to 2
0: 104
1: 107
2: 110
default: 113
}
104: bipush 97
106: ireturn
107: bipush 98
109: ireturn
110: bipush 99
112: ireturn
113: iconst_0
114: ireturn

目前还不清楚为什么 javac 会做出这样的选择。虽然 tableswitchlookupswitch 相比具有更高的基本占用空间(一个额外的 32 位字),但它的字节码仍然更短,即使对于两种 case 标签场景。

但是决策的一致性可以用第二个语句来显示,它总是具有相同的值范围,但编译为 lookupswitchtableswitch 仅取决于标签数量。所以当使用真正的稀疏值时:

public static char three(String s) {
switch(s) {
case "a": return 'a';
case "b": return 'b';
case "": return 0;
}
return 0;
}

编译为

public static char three(java.lang.String);
Code:
0: aload_0
1: astore_1
2: iconst_m1
3: istore_2
4: aload_1
5: invokevirtual #9 // Method java/lang/String.hashCode:()I
8: lookupswitch { // 3
0: 72
97: 44
98: 58
default: 83
}
44: aload_1
45: ldc #10 // String a
47: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
50: ifeq 83
53: iconst_0
54: istore_2
55: goto 83
58: aload_1
59: ldc #12 // String b
61: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
64: ifeq 83
67: iconst_1
68: istore_2
69: goto 83
72: aload_1
73: ldc #13 // String
75: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
78: ifeq 83
81: iconst_2
82: istore_2
83: iload_2
84: tableswitch { // 0 to 2
0: 112
1: 115
2: 118
default: 120
}
112: bipush 97
114: ireturn
115: bipush 98
117: ireturn
118: iconst_0
119: ireturn
120: iconst_0
121: ireturn

对稀疏哈希码使用 lookupswitch,但对第二个开关使用 tableswitch

关于java - String 是关于 switch 的数字类型并且总是编译为 lookupswitch 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51540892/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com