为什么jdk代码风格使用变量赋值并在同一行读取 - 例如。 (i=2) < 最大值

2024-04-17

我注意到,在 jdk 源代码中,更具体地说,在集合框架中,优先选择在表达式中读取变量之前分配变量。这只是一个简单的偏好还是有一些我不知道的更重要的事情?我能想到的原因之一是该变量仅在该表达式中使用。

由于我不习惯这种风格,所以读起来很困难。代码非常简洁。下面你可以看到一个例子取自java.util.HashMap.getNode()

Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 && ...) {
   ...
}

正如评论中已经提到的:Doug Lea 是集合框架和并发包的主要作者之一,他倾向于进行一些对于普通人来说可能看起来令人困惑(甚至违反直觉)的优化。

A "famous" example here is copying fields to local variables https://stackoverflow.com/questions/2785964/in-arrayblockingqueue-why-copy-final-member-field-into-local-final-variable in order to minimize the size of the bytecode, which is actually also done with the table field and the local tab variable in the example that you referred to!


对于非常简单的测试,无论访问是否“内联”,似乎都没有什么区别(指的是生成的字节码大小)。所以我尝试创建一个大致类似于结构的示例getNode您提到的方法:访问数组字段、长度检查、访问一个数组元素的字段......

  • The testSeparate方法将赋值和检查分开
  • The testInlined方法使用 if 风格的赋值
  • The testRepeated方法(作为反例)重复执行每个访问

代码:

class Node
{
    int k;
    int j;
}

public class AssignAndUseTestComplex
{
    public static void main(String[] args)
    {
        AssignAndUseTestComplex t = new AssignAndUseTestComplex();
        t.testSeparate(1);
        t.testInlined(1);
        t.testRepeated(1);
    }

    private Node table[] = new Node[] { new Node() };

    int testSeparate(int value)
    {
        Node[] tab = table;
        if (tab != null)
        {
            int n = tab.length;
            if (n > 0)
            {
                Node first = tab[(n-1)];
                if (first != null)
                {
                    return first.k+first.j;
                }
            }
        } 
        return 0;
    }

    int testInlined(int value)
    {
        Node[] tab; Node first, e; int n;
        if ((tab = table) != null && (n = tab.length) > 0 && 
            (first = tab[(n - 1)]) != null) {
            return first.k+first.j;
        }
        return 0;
    }

    int testRepeated(int value)
    {
        if (table != null)
        {
            if (table.length > 0)
            {
                if (table[(table.length-1)] != null)
                {
                    return table[(table.length-1)].k+table[(table.length-1)].j;
                }
            }
        } 
        return 0;
    }

}

以及生成的字节码:testSeparate方法用途41 条指令:

  int testSeparate(int);
    Code:
       0: aload_0
       1: getfield      #15                 // Field table:[Lstackoverflow/Node;
       4: astore_2
       5: aload_2
       6: ifnull        40
       9: aload_2
      10: arraylength
      11: istore_3
      12: iload_3
      13: ifle          40
      16: aload_2
      17: iload_3
      18: iconst_1
      19: isub
      20: aaload
      21: astore        4
      23: aload         4
      25: ifnull        40
      28: aload         4
      30: getfield      #37                 // Field stackoverflow/Node.k:I
      33: aload         4
      35: getfield      #41                 // Field stackoverflow/Node.j:I
      38: iadd
      39: ireturn
      40: iconst_0
      41: ireturn

The testInlined方法确实有点小,39 条指令

  int testInlined(int);
    Code:
       0: aload_0
       1: getfield      #15                 // Field table:[Lstackoverflow/Node;
       4: dup
       5: astore_2
       6: ifnull        38
       9: aload_2
      10: arraylength
      11: dup
      12: istore        5
      14: ifle          38
      17: aload_2
      18: iload         5
      20: iconst_1
      21: isub
      22: aaload
      23: dup
      24: astore_3
      25: ifnull        38
      28: aload_3
      29: getfield      #37                 // Field stackoverflow/Node.k:I
      32: aload_3
      33: getfield      #41                 // Field stackoverflow/Node.j:I
      36: iadd
      37: ireturn
      38: iconst_0
      39: ireturn

最后,testRepeated方法使用了高达63条指令

  int testRepeated(int);
    Code:
       0: aload_0
       1: getfield      #15                 // Field table:[Lstackoverflow/Node;
       4: ifnull        62
       7: aload_0
       8: getfield      #15                 // Field table:[Lstackoverflow/Node;
      11: arraylength
      12: ifle          62
      15: aload_0
      16: getfield      #15                 // Field table:[Lstackoverflow/Node;
      19: aload_0
      20: getfield      #15                 // Field table:[Lstackoverflow/Node;
      23: arraylength
      24: iconst_1
      25: isub
      26: aaload
      27: ifnull        62
      30: aload_0
      31: getfield      #15                 // Field table:[Lstackoverflow/Node;
      34: aload_0
      35: getfield      #15                 // Field table:[Lstackoverflow/Node;
      38: arraylength
      39: iconst_1
      40: isub
      41: aaload
      42: getfield      #37                 // Field stackoverflow/Node.k:I
      45: aload_0
      46: getfield      #15                 // Field table:[Lstackoverflow/Node;
      49: aload_0
      50: getfield      #15                 // Field table:[Lstackoverflow/Node;
      53: arraylength
      54: iconst_1
      55: isub
      56: aaload
      57: getfield      #41                 // Field stackoverflow/Node.j:I
      60: iadd
      61: ireturn
      62: iconst_0
      63: ireturn

因此,这种编写查询和赋值的“模糊”方式确实可以节省一些字节码,并且(考虑到有关在局部变量中存储字段的链接答案中的理由)这可能是使用的原因这种风格。

But...

无论如何:该方法执行几次后,JIT 将启动,生成的机器代码将与原始字节码“无关” - 而且我很确定所有三个版本实际上都是最终编译成相同的机器码。

所以底线是:不要使用这种风格。相反,只需写愚蠢的代码 http://www.oracle.com/technetwork/articles/java/devinsight-1-139780.html易于阅读和维护。您会知道什么时候轮到您使用此类“优化”。


编辑:一个简短的附录...

我做了进一步的测试,并进行了比较testSeparate and testInlined方法与实际情况有关机器码由 JIT 生成。

我修改了main方法有点,以防止不切实际的过度优化或 JIT 可能采取的其他捷径,但实际方法未作修改。

正如预期的那样:当使用热点反汇编 JVM 调用方法几千次时,-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:+PrintAssembly,那么这两种方法的实际机器码都是完全相同的.

因此,JIT 再次很好地完成了它的工作,程序员可以专注于编写readable代码(无论这意味着什么)。

...以及一个小的更正/澄清:

第三种方法我没有测试,testRepeated, 因为它是不等同到其他方法(因此,它can不会产生相同的机器代码)。顺便说一句,这是在局部变量中存储字段的策略的另一个小优点:它提供了 (very有限但有时很方便)形式的“线程安全": 确保数组的长度(如tab数组中的getNode的方法HashMap) 在方法执行时不能更改。

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

为什么jdk代码风格使用变量赋值并在同一行读取 - 例如。 (i=2) < 最大值 的相关文章

  • 如何使用Java泛型来避免强制转换?

    对于查询 提出于link https stackoverflow com questions 26192111 how to compare objects with different types 建议使用 Java 泛型以避免难以评估项
  • Joda Time 持续时间或间隔中的分钟数

    我有这个简单的代码 DateTime date new DateTime dateValue DateTime currentDate new DateTime System currentTimeMillis System out pri
  • 了解 hibernate @Type 注解

    来自休眠官方文档 http docs jboss org ejb3 app server HibernateAnnotations reference en html single d0e2018 org hibernate annotat
  • Linkify 是否适用于 Android 中的 TextView?

    我有这段代码适用于调用 EditText 的方法 我尝试对 TextView 使用相同的代码 但它不起作用 文本不会像 EditText 那样变成超链接 有人知道为什么吗 public class MainActivity extends
  • 如何使用 JRuby 创建 Java 小程序?

    我想使用 JRuby 创建一个 Java 小程序 也就是说 我想创建一个 Java 小程序 其中包含由 JRuby 运行的 Ruby 代码来完成所有 GUI 操作 我正在寻找一个简单的示例来说明如何开始 查看这些链接 来自我们代码库的 JR
  • Java applet - 以 png 格式保存图像

    我正在创建一个用于制作头像的简单小程序 您可以选择脸部 头发 眼睛等 然后将其作为 png 文件保存到光盘上 简单版本 为了简单起见 没有界面 如下所示 import java awt import java applet import j
  • 并发 log4j

    我有自己的日志引擎 它将日志写入带有阻塞队列的单独线程上 为了使用 标准软件 我正在考虑切换到 log4j 我不希望我的高并发软件因日志命令而变慢 这些日志命令在调用命令时将所有内容写入磁盘 log4j 可以用作垃圾箱吗 Log4j 是大多
  • csharp类可以像java类一样“继承”xml文档吗?

    我正在向一些csharp代码添加注释 并且我正在使用 net 或其他东西 提供的xml语言 我有一个接口和一些实现类 我在界面中有一个方法 它有一个注释 在实现类中没有对实现方法进行注释 当人们在java中这样做时 javadoc在生成文档
  • 在Android应用程序中导入Java项目?

    即使 Java 项目中的某些类在普通 Android 项目中无法识别 我是否可以在 Android 项目中使用 Java 项目 例如javax xml包 我认为有两种可能性 使用该 java 项目创建一个 jar 并将其导入到 androi
  • 多行 JTable 单元格在编辑期间不是多行的

    我正在开发一个应用程序 它有一个需要多行单元格的 JTable 因此 我扩展了 JTextArea 一切都显示出来了 但是当我尝试编辑单元格时 文本显示为单行 编辑后变为多行 我希望文本在编辑过程中保持多行 有没有办法做到这一点 创建您的表
  • JDA Events 更新版本后停止工作 [关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我有一个使用最新版本的 JDA 4 2 0 168 用 Ja va 开发的不和谐机器人 我的机器人中有几个事件 但只有一
  • 如何通过 Selenium 通过 XPath 访问 WebElement?

    我需要访问该网站上搜索结果的链接 并将它们放入WebElement 但我无法按班级或任何其他方式找到它们 使用时xpath MyWebDriver findElement By xpath div class inner results f
  • AngularJS 和 Webpack 集成

    我正在寻找一些使用帮助webpack http webpack github io docs 对于大型 AngularJS 应用程序 我们使用基于功能的文件夹结构 每个功能 页面都有一个模块 并且它们有控制器 指令 我已经成功配置了 web
  • 如何重定向到另一个 URI 并访问先前 modelAndView 中的对象

    我有以下代码 我想访问 nextPage jsp 上的 booleanValueObj 这是怎么做到的 该对象并不总是可用于每个请求的 nextPage 方法 因此 requestParam 似乎不合适 RequestMapping met
  • 有什么方法可以处理 HTTP/2 Goaway 在 HttpClient java 中收到的 IOException 吗?

    我在应用程序中进行 API 调用 在某个时候它会随机抛出java io IOException 149 222 1 1 553232 GOAWAY received 使用Java 11环境 无论如何要解决此异常而不是迁移到 Http 1 1
  • 带有 CompletableFuture 的 MDC 记录器

    我正在使用 MDC Logger 除了一种情况外 它对我来说非常适合 无论我们在代码中的何处使用 CompletableFuture 对于创建的线程 MDC 数据都不会传递到下一个线程 因此日志会失败 例如 在代码中我使用下面的代码片段来创
  • 像耐心/克朗代克纸牌游戏一样拖动节点

    我正在做克朗代克游戏 逻辑一切正常 我只是在使用 javafx 中的 UI 时遇到问题 我一直在尝试从 桌面堆 周围移动 拖动卡片 但没有达到预期的结果 我的卡片是一个 ImageView 里面有一个图像 这些卡片位于窗格内 Pane ta
  • GSON 解析空日期字段时抛出异常

    我正在使用 GSON 反序列化一些 JSON JSON 是 employee id 297 surname Maynard givenname Ron lastlogin 员工对象有一个日期字段lastlogin public class
  • Java 中客户端/服务器传输的压缩字符串

    我使用专有的客户端 服务器消息格式来限制我可以通过网络发送的内容 我无法发送序列化对象 我必须将消息中的数据存储为字符串 我发送的数据是大的逗号分隔值 我想在将数据作为字符串打包到消息中之前对其进行压缩 我尝试使用 Deflater Inf
  • 创建 JSON 对象并将其转换为 Java 中的 String

    我需要通过 http post 发送一个相当长的 JSON 标头 在Python中是这样的 self body header client self client name clientRevision self client versio

随机推荐

  • 如何将数组写入 Google 电子表格?

    我正在构建一个具有整数值的数组 并尝试一次性将其写入 Google 电子表格 var myArray new Array for i 1 i lt 100 i myArray i i ss getRange 8 4 1 100 setVal
  • 如何通过连接到 Linux 计算机的 GSM/GPRS 调制解调器发送彩信? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我有一个目录 其中包含 50 个图像
  • 9 补丁填充区域不适用于多个比例区域

    在我的 9 补丁中 我添加了一个填充区域 作为占据红色矩形下方大部分宽度的内容 然而 正如比例图像所示 它不起作用 如果我删除箭头所示的比例区域 那么它就可以完美工作 我做错了什么或者不理解 9 补丁吗 Thanks 我找到了一个相关的答案
  • Rails:form_for复选框设置为true或false,无论该框被选中/取消选中

    我有一个名为 users 的模型 它有 2 个布尔属性send email and send text 我有一个编辑用户模型的表单 我希望它根据该框是否选中 取消选中将这些属性设置为 true false 这是我的表格 div class
  • C# 泛型类型推断与协方差 - bug 或限制

    当具有依赖参数的泛型方法推断类型时 它在某些情况下会给出意外的结果 如果我明确指定类型 则一切都可以正常工作 无需任何进一步的更改 IEnumerable
  • 两个相同的 git 存储库

    我们在一个大团队中使用 git 作为项目的源代码 现在大约有一半的开发商正在离开 进入项目的现场阶段 此阶段将涉及大量开发 即许多提交 同时剩下的另一半也将继续编写代码 不幸的是 现场的互联网连接时断时续且不可靠 然而 至关重要的是 现场团
  • Julia JUMP Gurobi MIP - 查询并存储最佳目标并在运行时绑定

    我通过 Julia 中的 JuMP 包使用 Gurobi 来解决混合整数程序 我想获得一个图表像这个 https i stack imgur com 427LC png 其中还提供了基于 Python 的解决方案 也已在古罗比社区形式 ht
  • 如何在 Swift 中访问深度嵌套字典

    我的应用程序中有一个非常复杂的数据结构 我需要对其进行操作 我正在尝试跟踪玩家的花园中有多少种错误 虫子有十种 每种有十种图案 每种图案有十种颜色 因此可能存在 1000 个独特的错误 我想跟踪玩家拥有的每种类型的错误数量 嵌套字典如下所示
  • WPF MVVM EF 简单示例

    当我正在努力将数据插入视图模型时 我希望有人可以帮助我开始这个简单的 WPF MVVM 示例 我有一个 SQL 表温度 其中每个记录都有一个时间戳 然后是一个数值 例如 记录时间 温度2013年1月1日 00 00 6001 01 2013
  • 如何在多个Lua State(多线程)之间传递数据?

    我在中启动Redis连接池redis lua 通过从 C 调用 我得到了redis lua state 此 Lua 状态全局启动一次 仅在其他线程中启动get从中 当有一个 HTTP 请求 工作线程 时 我需要从redis lua stat
  • 将角度定量数据转换为定性图像

    我是一名晶体学家 试图从多达 5000 个文件中分析晶体方向 Matlab 能否转换表格中的角度值 如下所示 进入一个看起来像这样的表 这是一个基于 Lakesh 想法的更具体的例子 然而 这将处理任意数量的旋转 首先从中间有一条带的基本圆
  • 如何将地名词典或词典表示为 crf++ 中的特征?

    如何使用地名词典或词典作为功能CRF https taku910 github io crfpp 详细说明 假设我想对人名进行 NER 并且我有一个包含常见人名的地名词典 或字典 我想使用这个地名词典作为 crf 的输入 我该怎么做 我正在
  • 使用来自不同模块的 python 装饰器函数

    我想使用另一个模块中的函数作为装饰器 但我需要它来操作当前模块的全局命名空间 例如 我希望能够从这里开始 class SomeClass pass root SomeClass to this from othermodule import
  • 什么时候可以比较 C++ 中同一对象的指针?

    例如 我有一些类层次结构 可能具有各种继承 公共 私有 公共虚拟 多重继承等 class A int a public virtual A class B public A int b class C public virtual B in
  • iphone - 什么消耗更少的电池?开始监控位置更改或开始监控区域?

    我希望您向我提供关于哪种方法消耗更少电池的反馈 我的应用程序将在后台运行 并会随着位置变化而唤醒 所以我想使用消耗更少电池的方法 有什么想法吗 Thanks 这些选择都不会增加或减少电池消耗 为了让您的应用收到任何位置更新的通知 无论是区域
  • 在java中查找(hh mm ss)格式的平均时间

    我想以 hh mm ss 格式计算三个时间的平均值 我尝试了以下代码 public String calculateAverageOfTime String timeInHHmmss 08 00 00 08 00 00 08 00 00 S
  • 处理获取响应的正确方法是什么

    我有以下用于处理 Magento 2 REST API 的代码 return new Promise resolve reject gt fetch uri method headers body JSON stringify data t
  • Nokogiri 本机扩展无法构建(不是 libxml2 或 libxslt 缺失问题)

    正如标题所示 它似乎并没有因为缺少 libxml2 或 libxslt 而失败 我不太确定该做什么make的错误 明白了吗 因为问题出在make期间 呵呵 无论如何 这是我得到的输出 任何想法 将不胜感激 Building native e
  • Mvc RenderAction 性能与 RenderPartial

    您好 我想知道 mvc C s 渲染操作是否与直接调用我的评论中的部分一样有效 如果不是 那么效率有多低 RenderAction肯定会慢于RenderPartial 差异取决于子操作相对于父操作执行的额外工作量 对于非常简单的子动作 差异
  • 为什么jdk代码风格使用变量赋值并在同一行读取 - 例如。 (i=2) < 最大值

    我注意到 在 jdk 源代码中 更具体地说 在集合框架中 优先选择在表达式中读取变量之前分配变量 这只是一个简单的偏好还是有一些我不知道的更重要的事情 我能想到的原因之一是该变量仅在该表达式中使用 由于我不习惯这种风格 所以读起来很困难 代