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

2024-03-01

以下代码返回是否给定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 编译开关 https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-3.html#jvms-3.10"

switch 语句的编译使用桌面开关 and 查找开关指示。

Moreover

桌面开关 and 查找开关指令仅适用于int data.

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

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

其他数字类型必须缩小为 int 类型才能在 switch 中使用。

问题一:
Is String在这种情况下也是数字类型吗?或者我错过了什么?

A javap -c在课堂上SwitchOnString shows:

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值用作int- 的按键cases。这可能匹配:

The 查找开关指令对int键(案例的值 标签) ...

继续到桌面开关 and 查找开关JMS 说:

The 桌面开关当开关的情况可以时使用指令 可以有效地表示为目标偏移表中的索引。 (...) 当开关的情况稀疏时,表表示为 这桌面开关教学在空间方面变得低效。这查找开关可以使用指令来代替。

如果我做对了,那么案例越稀疏,可能性就越大查找开关将会被使用。

问题2:
但看看字节码:
两个字符串大小写是否足够稀疏以进行编译switch to 查找开关?或者每个开关都会打开String被编译为查找开关?


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

在这方面,JVMS 声明“其他数字类型必须缩小为类型”int用于switch” 并没有说 Java 编程语言会做这样的转换,也没有说String or Enum是数字类型。 IE。long, float and double are数字类型,但不支持将它们与switchJava 编程语言中的语句。

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

正如“为什么 switch on String 编译成两个 switch https://stackoverflow.com/q/25568639/2711488” and “Java 7 String switch 反编译:意外指令 https://stackoverflow.com/q/6956792/2711488”, javac目前编译时会在字节码级别生成两个 switch 指令switch over String值(ECJ 也生成两条指令,但细节可能有所不同)。

然后,编译器必须选择一个lookupswitch https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-6.html#jvms-6.5.lookupswitch or tableswitch https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-6.html#jvms-6.5.tableswitch操作说明。javac确实使用tableswitch当数字不稀疏时,但前提是语句具有两个以上的 case 标签。

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

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

I get

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

I get

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做出这样的选择。尽管tableswitch与相比,具有更高的基本占用空间(一个额外的 32 位字)lookupswitch,即使对于两个,它的字节码仍然会更短case标签场景。

但是决策的一致性可以通过第二条语句来显示,该语句始终具有相同的值范围,但编译为lookupswitch or 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

using lookupswitch对于稀疏哈希码,但是tableswitch对于第二个开关。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

String 是关于 switch 的数字类型并且总是编译为 LookupSwitch 吗? 的相关文章

随机推荐

  • TensorFlow教程中的next_batchbatch_xs,batch_ys = mnist.train.next_batch(100)来自哪里?

    我正在尝试 TensorFlow 教程 但不明白这一行中的 next batch 来自哪里 batch xs batch ys mnist train next batch 100 我在看 from tensorflow examples
  • Android 中如何读取文本文件? [复制]

    这个问题在这里已经有答案了 我将详细信息保存在 out txt 文件中 该文件已在 data data new android files out txt 中创建了一个文本文件 我可以将信息附加到文本中 但是我无法读取该文件 我使用以下过程
  • 无法在 Android 上选择多个图像上传 - Chrome 网络浏览器

    我想创建一个网络应用程序 用户可以从手机的图片库中选择并上传多个图像 这个功能在iOS上运行良好 但在Android上似乎被破坏了
  • JBoss 4:在哪里部署全局过滤器?

    我想使用 全局 HTTP 过滤器 因此 我编辑了deploy jboss web deployer conf web xml并补充道
  • 模拟Winforms按钮点击动画

    我有一个按钮 按钮内有一个图像控件 当用户单击图像时 我想为按钮设置动画 以便看起来按钮被按下 我并不真正关心实际的按钮按下事件是否触发 这是我想看到的按钮按下的幻觉 注意 我在网上看到的唯一选项涉及直接写入 Windows API 我真的
  • 如何根据简单的多边形绘制图像?

    我想将一个大致矩形区域复制到一个矩形区域 例子 两个区域均由其角点定义 保持大体方向 不翻转等 简单地旋转源图像是行不通的 因为相对边的长度可能不同 到目前为止 我发现没有办法在纯 C 中做到这一点 手动像素复制除外 所以我想我必须求助于
  • 在java中创建指定名称的临时文件

    我有一个 Byte 数组 我想将其内容放入临时文件中 我尝试这样做 try tempFile File createTempFile tmp null FileOutputStream fos new FileOutputStream te
  • 调试时从数据行获取所有列名/列值

    我正在创建一个数据行的模拟实例以进行测试 我尝试从数据库中复制的行包含 37 列 其中包含不同的变量 调试时是否有机会以干净的文本形式获取信息 以便简单地编辑我的模拟对象 我必须调整尼基尔的演员阵容 var colNames dr Tabl
  • 使用 C# 确定谁打开了文件

    使用 C 如何获取有关谁打开了文件的信息 用户名和机器名就足够了 以防万一 我有 Windows 工作站通过 Samba 访问 Linux 文件服务器上的文件 我在工作站上运行的程序中需要此信息 核心 NET 库没有任何方法可以做到这一点
  • 访问指针指向的整数数组时,“sizeof”对不完整类型“int[]”的无效应用

    我正在尝试学习 C 中的指针 并正在编写这个小整数数组指针练习 但遇到了无效的应用程序sizeof不完整类型int 问题 请告诉我哪里出了问题以及如何解决 谢谢 include
  • 在非标准位置安装带有库的 sf 包

    所需的库位于非标准位置 我可以通过以下命令安装 rgdal install packages rgdal type source configure args c with gdal config home programs anacond
  • 您会使用 实现轻量级 XML 解析器吗?

    如果您必须实现一个轻量级 XML 解析器 您会选择使用正则表达式吗 在我的例子中 XML 解析是最简单的 只有标签和文本内容 没有命名空间 没有属性 没有模式支持 当然是在一开始 但也许 我认为学习新的 C 0x 库对我来说是一个很好的练习
  • 将日期中的 NA 替换为另一个日期

    Data DB1 lt data frame orderItemID 1 10 orderDate c 2013 01 21 2013 03 31 2013 04 12 2013 06 01 2014 01 01 2014 02 19 20
  • WAMP服务器呈绿色但只得到404

    好吧 女士们先生们 我有一个很令人困惑的问题 我在工作中的 WIN7 机器上安装了 WAMP 服务器 一切都工作正常 有几个星期没有使用它 因为我被其他事情吸引了 有一天 我尝试启动它 图标是绿色的 我认为我们做得很好 然后我尝试打开 lo
  • 邮件脚本 - 解析错误:语法错误,意外的“=”[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试从包含下拉列表的 html 表单设置一个简单的邮件脚本 但在声明所选变量的行上我收到一条错误消息 解析错误 语法错误 意外的 我
  • 如何手动解密 EncryptedAssertion

    我想解密 EncryptedAssertion 我尝试使用 OpenSaml Decrypter 但它对我不起作用 我无法解密 EncryptedData 我已经问过这个问题了 EncryptedAssertion 解密失败 https s
  • Selenium:遍历元素列表

    我正在使用 XPath CSS 和 Selenium 来定位网站上的元素 我想创建一种方法 在该方法中迭代定位器列表 XPath CSS 程序选择有效的一个 换句话说 它从定位器一开始 如果定位器存在 则返回 true 并存在循环 否则 它
  • winHTTP GET 请求 C++

    我就开门见山吧 这就是浏览器请求的样子 获取 index html HTTP 1 1 这就是 winHTTP 的作用 GET http site com index html http site com index htmlHTTP 1 1
  • Java用户类

    如何解析java用户类和JDBC用户类 问题是当我用完 put 时 sql 中有数据 例如 public User authenctication String eMail String password try con DriverMan
  • String 是关于 switch 的数字类型并且总是编译为 LookupSwitch 吗?

    以下代码返回是否给定String s等于任何其他硬编码字符串 该方法使用switch 这样做的声明 public class SwitchOnString public static boolean equalsAny String s s