java模式:什么时候使用临时变量是有意义的

时间:2012-02-26 18:20:59

标签: java design-patterns

所以我发现自己经常做类似下面的模式。而不是:

if (map.containsKey(someKey)) {
    Value someValue = map.get(someKey);
    ...
}

为了不遍历地图两次(因为我知道我的地图不存储空值),我会这样做:

Value someValue = map.get(someKey);
if (someValue != null) {
    ...
}

在我看来,这似乎是一个有价值的模式,因为Map操作执行了大量的操作,而我认为优化器不够智能,无法优化它。

但后来我发现自己在其他情况下也会做类似的模式。例如,我应该将someMethod()结果存储在临时变量中,而不是将调用两次?很明显,如果有副作用,我不能两次调用someMethod()但是从优化的角度来看只调用一次是否有意义?

if (someObject.someMethod() != null) {
    processSomehow(someObject.someMethod());
}

我知道这与“非建设性”问题接壤,因此我正在寻找能够提供一些事实和参考的答案,而不仅仅是猜想。

  • 什么时候这样做才有意义?什么时候不这样做?
  • 如何评估someMethod()的“费用”以确定何时应使用临时变量?
  • 对于诸如get方法之类的简单方法,这可能会妨碍热点编译器或优化器,并实际产生效率较低的代码吗?

对于后代,我不是在问“我怎样才能提高现有程序的速度”。我试图找出“在编写未来代码时我应该使用什么模式”。

感谢您提供任何信息。

6 个答案:

答案 0 :(得分:11)

  

如何评估someMethod()的“成本”以确定何时应使用临时变量?

只需看看someMethod()的实施情况。如果它只返回一个字段 - 就像通常的getter方法那样,就没有必要声明一个局部变量(性能明智)。如果方法创建对象,调用数据库或读取文件内容,则通常明智地缓存返回值。

就个人而言,我并不关心因宣布临时变量而导致的性能问题。我只保留返回值而不是两次调用方法。代码更易于阅读和理解(除非您将所有这些变量命名为temp ;-))

答案 1 :(得分:5)

  

显然,如果有副作用,我不能两次调用someMethod()但是从优化的角度来看只调用一次是否有意义?

当您使用分析器证明它很重要时。但是习惯连续两次不调用方法,因为它会使维护更容易(如果检查发生变化而你忘了在两个地方都改变它?)。大量使用临时变量,它们非常便宜。

答案 2 :(得分:5)

作为一般规则,我需要多次使用本地var。你可以给那个本地var一个漂亮的名字,每个人都知道这个var是什么。从性能的角度来看:本地变量很便宜,方法可能很复杂。

特别是当someMethod()像同步方法一样昂贵时,这应该会更好。但是当someMethod()是一个同步方法时,它意图由多个并发线程调用,然后调用该方法两次或存储其返回值并重用它会有所不同......

......另一点需要提及的是,后续方法调用不必返回相同的数据。因此,使用/不使用local var的示例并不总是有效替代后续方法调用。但我认为你已经考虑过了。

答案 3 :(得分:2)

对我来说,这一切都归结为程序的逻辑而不是性能。所以

if (map.containsKey(someKey)) {
    Value someValue = map.get(someKey);
    ...
}
在所有情况下,

等同于该代码:

Value someValue = map.get(someKey);
if (someValue != null) {
    ...
}

考虑一个地图,其中 的值对于给定的密钥为空。如果...部分null安全,则代码的行为会有所不同。

此代码也有某种逻辑问题。考虑一下:

BufferedRead reader = ...;
if (reader.readLine() != null) {
    processSomehow(reader.readLine());
}

很明显,readLine 必须被调用两次。这种代码的另一个问题是多线程:代码期望值不会改变,但另一个线程可能会这样做。

摘要让代码尽可能清晰准确。这意味着,如果您希望某个值不会更改并且您需要多次,那么请使用变量告诉任何人。

答案 4 :(得分:2)

有几个人引用Knuth(隐式或明确地引用),但我认为这总是有意义的设计模式(以及null是允许键的情况,as pointed out by A.H.变得更加清晰)。

在我看来,它类似于在C ++中通过const引用而不是通过值传递变量的情况。这样做有两个原因:

  1. 表示该值不会发生变化
  2. 但它完成的主要原因是它的速度略快。
  3. 对于大多数个别调用并不重要,但是当每个例程按值传递每个参数时,您会注意到它。这是一个很好的习惯。你提到的案例并不常见,所以它并不重要,但我认为它仍然是一个好习惯。

答案 5 :(得分:1)

让我想起关于类似.NET Dictionary使用方式的讨论:What is more efficient: Dictionary TryGetValue or ContainsKey+Item?

一般情况下,正如评论中所指出的那样,除非您在分析器下使用真实数据运行它,否则您不知道程序中的性能热点。

此外,通常,由于可能存在副作用,命令式语言中的编译器无法优化重复调用。每次调用函数都可以返回一个新值。顺便说一句,这也是你自己不应该依赖重复呼叫的原因之一。今天也许你知道函数或属性是无副作用的,重复调用返回相同的值,但明天你改变它并在里面添加一些随机生成器,所有重复调用的逻辑都将被破坏。