Java 对对象调用getter与将其存储为局部变量(内存占用、性能)

Java 对对象调用getter与将其存储为局部变量(内存占用、性能),java,performance,readability,memory-consumption,Java,Performance,Readability,Memory Consumption,在下面的代码段中,我们进行了两次调用listType.getDescription(): for (ListType listType: this.listTypeManager.getSelectableListTypes()) { if (listType.getDescription() != null) { children.add(new SelectItem( listType.getId() , listType.getDescription()))

在下面的代码段中,我们进行了两次调用
listType.getDescription()

for (ListType listType: this.listTypeManager.getSelectableListTypes())
{
    if (listType.getDescription() != null)
    {
        children.add(new SelectItem( listType.getId() , listType.getDescription()));
    }
}
我倾向于重构代码以使用单个变量:

for (ListType listType: this.listTypeManager.getSelectableListTypes())
{
    String description = listType.getDescription();

    if (description != null)
    {
        children.add(new SelectItem(listType.getId() ,description));
    }
}
我的理解是JVM以某种方式针对原始代码进行了优化,特别是嵌套调用,如
children.add(new-SelectItem(listType.getId(),listType.getDescription())

比较这两个选项,哪一个是首选方法,为什么?这涉及到内存占用、性能、可读性/易用性,以及我现在没有想到的其他方面


什么时候后一个代码段比前一个代码段更有优势,也就是说,当使用临时局部变量变得更可取时,
listType.getDescription()
调用的数量是否(近似)如
listType.getDescription()
总是需要一些堆栈操作来存储
这个
对象?

我认为第二种方法肯定更好,因为它提高了代码的可读性和可维护性,这是这里最重要的。除非你编写一个每毫秒都很重要的应用程序,否则这种微优化在任何方面都不会真正帮助你。

我不确定哪一种都是首选。我更喜欢的是清晰可读的代码,而不是性能代码,尤其是在性能增益可以忽略不计的情况下。在这种情况下,我怀疑几乎没有明显的区别(特别是考虑到JVM的优化和代码重写功能)

在命令式语言的上下文中,函数调用返回的值不能被记忆(请参阅),因为不能保证函数没有副作用。因此,您的策略确实避免了函数调用,而代价是分配一个临时变量来存储对函数调用返回的值的引用。
除了稍微更高效(除非函数在循环中多次调用,否则这并不重要),我会选择您的风格,因为代码可读性更好

我几乎总是喜欢局部变量的解决方案

内存占用 单个局部变量的开销为4或8字节。它是一个引用,没有递归,所以我们忽略它

演出 如果这是一个简单的getter,JVM可以自己记忆它,所以没有区别。如果这是一个无法优化的昂贵呼叫,手动记忆会使它更快

可读性 遵循原则。在您的例子中,它几乎不重要,因为局部变量名的字符长度与方法调用的长度一样长,但对于更复杂的内容,它的可读性很好,因为您不必在两个表达式之间进行转换。如果您知道它们是相同的,那么使用局部变量来说明

正确性 假设您的
SelectItem
不接受
null
s,并且您的程序是多线程的。
listType.getDescription()
的值在此期间可能会发生变化,您将被烤熟

调试 拥有包含有趣值的局部变量是一个优势


省去局部变量的唯一方法是保存一行。所以我只会在不重要的情况下这样做:

  • 极短表达
  • 不可能同时进行修改
  • 简单私人终结者

我同意局部变量方法的可读性,前提是局部变量的名称是自记录的。称之为“描述”是不够的(哪种描述?)。将其称为“selectableListTypeDescription”会使其更加清晰。我要强调for循环中递增的变量应该命名为“selectableListType”(特别是当“listTypeManager”具有其他ListType的访问器时)


另一个原因是,如果不能保证这是单线程的,或者您的列表是不可变的。

我完全同意。关于可读性,我想补充一点: 我看到很多程序员都在做这样的事情:

如果(item.getFirst().getSecond().getThird().getForth()==1||

item.getFirst().getSecond().getThird().getForth()==2||

item.getFirst().getSecond().getThird().getForth()==3)

甚至更糟的是:

item.getFirst().getSecond().getThird().setForth(item2.getFirst().getSecond().getThird().getForth())


如果您多次调用相同的10个getter链,请使用中间变量。它只是更容易阅读和调试

Type2也是“用查询替换临时”的一个例子。如果调用的数量足够高,JIT会尽可能将其记录下来。其余的请看我的答案。在“正确性”一节中关于多线程问题的要点很好。谢谢