19

假设我的课程为:

public class Age {

    private int age;

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

}

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

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

哪个最好,为什么?

4

13 回答 13

11

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

于 2010-05-12T17:20:43.040 回答
10

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

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

于 2010-05-12T17:19:21.733 回答
3

getAge()多次调用该方法可能会有一些性能开销,但我建议您考虑微优化剧院的悲惨悲剧

于 2010-05-12T17:21:39.513 回答
3

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

一般来说,如果您只是返回一个属性,您可以将调用标记为最终调用(如果您不提供实际接口)。这应该会降低调用成本,因为编译器更有可能内联函数。

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

于 2010-05-12T17:21:58.167 回答
2

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

过早的优化是万恶之源。

于 2010-05-12T17:22:48.673 回答
1

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

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

但是,如果您的 getAge 是一些花哨的东西,可以进行大量计算或访问 IO 资源,例如:

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

那么可以肯定的是,缓存 de 结果并使用它是一个好主意。因为如果您调用它 30 次,您的代码将需要 1 分钟才能完成。

于 2010-05-12T17:33:04.733 回答
1

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

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

于 2010-05-12T17:39:35.513 回答
1
get attrs () {
    const elem = {
        'A' :   ${"[data-test='load-more-button']}
        'B' :   $$('[data-test="product-catalog-row"]'),
    }
    return elem
} 

Is it possible (at this line) to call the get() and assign it to a variable that can be passed to the funcs() rather than passing attrs = this.attr? The funcs are in the same files as the get(). What is the correct syntax?

func1(attrs = this.attr){attr.A}
func2(attrs = this.attr){attr.B}
于 2020-11-23T03:12:11.087 回答
0

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

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

我真的很希望编程语言引入一个“超级私有”属性/字段修饰符,它基本上说“你只能通过它的访问器访问这个属性”。

于 2010-05-21T14:46:51.020 回答
0

对于像这样的简单案例,我会选择在代码方面看起来最好的案例。

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

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

因为编译器可能无法确定循环体是否会影响列表的大小。一个正确实现的列表应该总是能够很容易地判断它的大小,但在其他示例中(或者在处理实现不佳的集合时)它可能会更糟。

一般来说,如果一个方法计算量很大,你可以使用memoization,这基本上意味着你缓存已经计算的输入/输出值。

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

这种技术将“节省回报价值换效率”推到了方法本身,从而简化了维护bla bla bla ...

于 2010-05-12T17:18:31.713 回答
0

我认为您在运行时不会看到任何区别 - 假设您不创建多个 Age 类。

于 2010-05-12T17:18:47.313 回答
0

除非您在 getAge() 方法中执行许多操作,否则您不会看到性能有太大变化。

于 2010-05-13T08:29:59.050 回答
0

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

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

这意味着 bean 属性的 get/set(值只是存储和检索,而不是计算)非常便宜,您应该每次都进行调用,并期望 JVM 检测可能的优化并应用它们。

于 2010-05-13T06:45:54.160 回答