多次调用getter或调用一次并分配给变量?

时间:2010-05-12 17:16:25

标签: java premature-optimization

假设我的课程为:

public class Age {

    private int age;

    public int getAge() {
       return this.age;
    }

}

在我的Main课程中,我多次调用getAge()方法。

所以我想知道调用这么多次或调用一次并将其分配给某个变量并使用该变量是明智的。

哪个最好,为什么?

13 个答案:

答案 0 :(得分:10)

这可能是您在知道需要之前进行优化的情况。该值只是一个整数,因此如果将值存储在多个位置,它不会占用大量内存。同时,这是一个非常简单的方法调用,不会花费太多时间来执行。以您认为最具可读性的方式编写它。然后,在拥有可靠的代码版本之后,您可以使用分析工具来查看是否存在明显的差异。

答案 1 :(得分:7)

不要尝试对此进行微观优化,除非您在分析时发现它确实是一个瓶颈。我使用getAge()访问器方法,因为它很可能是最易维护和最明显的解决方案。

话虽如此,这两种方法的表现可能完全相同。在运行时,JIT很可能完全优化掉getAge()调用,因此在两种情况下它都是单一的原始访问。

答案 2 :(得分:3)

多次调用getAge()方法可能会有一些性能负担,但我建议您考虑The Sad Tragedy of Micro-Optimization Theater

答案 3 :(得分:3)

这是您作为API编写者必须向调用者指示的内容。

通常,如果您只是返回一个属性,则可以将该调用标记为final(如果您没有提供实际的接口)。这应该可以降低调用的成本,因为编译器更有可能内联函数。

如果计算属性的成本很高(例如,字符串查找),请在JAvaDocs中为该方法记录,并向调用者指示他们可能希望获取一次值并将其缓存。

答案 4 :(得分:2)

不要打扰。像这样绝对不值得微优化。等到你完成代码,然后它运行得太慢,然后拿出一个分析器,然后研究分析器告诉你的问题来源。

过早优化是万恶之源。

答案 5 :(得分:1)

我会尝试用代码示例说出其他答案已经说过的内容。

如果您提出getAge()的调用非常简单,调用它的成本几乎为零。在这种情况下不要打扰它。

但是如果你的getAge很有意思,可以进行大量的计算或访问IO资源,比如:

public int getAge() {
   return slowService.calculateAgeByBirthDate(birthDate); // it takes 2 seconds to execute for every call
}

然后确定缓存de result并使用它是个好主意。因为如果你打电话30次,你的代码需要1分钟才能完成。

答案 6 :(得分:1)

根据应用程序的设计方式,这两个选项实际上可能会产生不同的结果!如果您使用的年龄实例是共享且可变的,则应用程序中的不同位置可能会在您对getAge()的调用之间更改其值。在这种情况下,确定哪个是您的代码的最佳选项是正确的问题,由您决定。正如古老的谚语所说:“首先要做对,然后快点”。并且,正如其他人已经提到的那样,在这种情况下,您可能不需要担心“快速”部分。

一个相关的例子是当你在迭代它时更改一个集合。您必须遍历快照才能获得ConcurrentModificationException

答案 7 :(得分:1)

get attrs () {
    const elem = {
        'A' :   ${"[data-test='load-more-button']}
        'B' :   $$('[data-test="product-catalog-row"]'),
    }
    return elem
} 

是否可以(在此行)调用get()并将其分配给可以传递给funcs()而不是传递attrs = this.attr的变量?这些功能与get()在同一文件中。正确的语法是什么?

func1(attrs = this.attr){attr.A}
func2(attrs = this.attr){attr.B}

答案 8 :(得分:0)

对于这样一个简单的案例,我会选择看起来最好的代码。

在某些情况下,建议您调用一次并读取保存的返回值,例如

for (int i = 0; i < list.size(); i++)
    doSomethingThatDoesNotAffectSizeOfList();

因为编译器可能无法确定循环体是否影响列表的大小。正确实现的列表应该始终能够轻松地告诉它的大小,但在其他示例中(或者在处理实现不佳的集合时),它可能会更糟。

通常,如果方法计算量很大,则可以使用memoization,这基本上意味着您缓存已计算的输入/输出值。

  

记忆功能“记住”与某些特定输入相对应的结果。使用记忆输入的后续调用将返回记住的结果而不是重新计算结果,从而消除了除了使用这些参数对函数进行的第一次调用之外的所有参数的调用的主要成本。

这项技术将“为效率而保留的价值”推向了方法本身,从而减轻了维护责任......

答案 9 :(得分:0)

我认为你会发现在运行时没有区别 - 假设你没有创建多个Age类。

答案 10 :(得分:0)

棘手的部分是理解现代JVM在根据运行时可用的知识编译字节代码时进行积极的优化。

例如,如果给定的方法没有在子类中重写,则可以将其视为与final方法完全相同,允许JVM在调用方法中内联其代码的副本,而不是显式执行方法调用。 (如果条件发生变化,那么这些类将被简单地视为新类,因此稍后会根据新条件重新编译)。

这意味着为bean attibutes获取/设置(其中值只是存储和检索,而不是计算)非常便宜,您应该每次都进行调用并期望JVM检测可能的优化并应用它们。 / p>

答案 11 :(得分:0)

除非你在getAge()方法中做了很多操作,否则你的性能不会有太大变化。

答案 12 :(得分:0)

在这种情况下,我建议不要考虑性能,看看可用性和代码重用。您当前的实现是最简单的getter,它返回一个整数。

但是,如果在某个地方存储一个人的生日并希望动态生成年龄呢?如果您只是直接调用该属性,则会被迫重构您的代码。但是,改变getAge()的内部结构,你可以将计算放在那里,然后就完成了。

我真的很喜欢编程语言来引入一个'超私有'属性/字段修饰符,一个基本上说'你只能通过它的访问者访问这个属性'。