Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 字符串是关于开关的数字类型,并且总是编译为lookupswitch吗?_Java_String_Jvm_Switch Statement - Fatal编程技术网

Java 字符串是关于开关的数字类型,并且总是编译为lookupswitch吗?

Java 字符串是关于开关的数字类型,并且总是编译为lookupswitch吗?,java,string,jvm,switch-statement,Java,String,Jvm,Switch Statement,以下代码返回给定的字符串s是否等于任何其他硬编码字符串。该方法使用开关-语句执行此操作: public class SwitchOnString { public static boolean equalsAny(String s) { switch (s) { case "string 1": return true; case "string 2": return true;

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

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)”

switch语句的编译使用tableswitch和lookupswitch 指示

而且

表开关和查找开关指令仅对
int
数据操作

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

唯一一句间接接近的话是:

其他数字类型必须缩小为int类型,以便在开关中使用

问题1:
在此上下文中,
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
值用作
case
s的
int
-键。这可能匹配:

lookupswitch指令对
int
键(大小写的值 标签)

转到tableswitch和lookupswitch,JMS说:

表开关指令在开关外壳可以打开时使用 有效地表示为目标偏移量表中的索引。(…) 如果开关的情况比较稀疏,则 tableswitch指令在空间方面效率低下 可以改用lookupswitch指令

如果我做对了,那么案例越稀疏,就越有可能使用查找开关

问题2:
但是看看字节码:

两个字符串大小写是否足够稀疏以编译
switch
到lookupswitch?或者
string
上的每个开关是否都会编译到lookupswitch?

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

在这方面,JVMS声明“其他数字类型必须缩小到
int
类型,以便在
开关中使用。”没有说Java编程语言将进行这样的转换,也没有说
String
Enum
是数字类型。例如
long
float
double
是数字类型,但是Java编程语言中不支持将它们与
switch
语句一起使用

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

如“”和“”
javac
中所述,当前在编译
switch
over
String
值时在字节码级别生成两条开关指令(ECJ也生成两条指令,但细节可能不同)

然后,编译器必须选择or指令。
javac
在数字不稀疏时使用
tableswitch
,但只有在语句有两个以上的大小写标签时才使用

因此,当我编译以下方法时:

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

但是当我编译的时候

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

现在还不清楚为什么
javac
会做出这样的选择。虽然与
lookupswitch
相比,
tableswitch
具有更高的基本占用空间(一个额外的32位字),但它的字节码仍然更短,即使对于两个
案例
标签场景也是如此

但第二条语句可以显示决策的一致性,它始终具有相同的值范围,但编译为
lookupswitch
tableswitch
,这仅取决于标签的数量。因此,当使用真正稀疏的值时:

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

字符串不是数字类型,但它们的散列码是。但是还有一个
equals
来禁止相同的散列码。我不太喜欢语法糖,这是(对我来说)唯一的用例就是当我必须使用字符串并对原始数据进行处理时。@DaveNewton哪个
等于
你的意思是什么?看看
String.equals
,我不明白它怎么会“禁止相同的哈希代码”“.你能再解释一下吗?你的意思是,字符串
大小写
s通常是稀疏的,因为它们的
哈希代码
s之间可能存在冲突?虽然随机字符串可能有不同的哈希代码,但有些小字符串有相同的哈希代码,因此还需要使用equals来检查字符串的内容是否符合预期。@PeterLawrey Thx解释。现在我明白了
equals
旨在解决哈希代码冲突。这感觉像是一个问题。一个switch语句实际上无法进行这种匹配。如果需要一组字符串,请使用
set
。我很想回答这个问题,但我很担心。谢谢你的深入分析。关于我的第一个问题,我知道
String
Enum
不同,它不是数字类型。对于如何切换
字符串
没有进一步的规范。这取决于编译器。第二个问题可以快速回答:否-这取决于标签的数量。我说得对吗?@LuCio是的,你说得对。更准确地说,第二个问题的答案是在
javac
的情况下,它取决于标签的数量。其他编译器可能会有不同的处理方法。请确认和澄清。
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