所以我发现自己经常做类似下面的模式。而不是:
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()
的“费用”以确定何时应使用临时变量?对于后代,我不是在问“我怎样才能提高现有程序的速度”。我试图找出“在编写未来代码时我应该使用什么模式”。
感谢您提供任何信息。
答案 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引用而不是通过值传递变量的情况。这样做有两个原因:
对于大多数个别调用并不重要,但是当每个例程按值传递每个参数时,您会注意到它。这是一个很好的习惯。你提到的案例并不常见,所以它并不重要,但我认为它仍然是一个好习惯。
答案 5 :(得分:1)
让我想起关于类似.NET Dictionary
使用方式的讨论:What is more efficient: Dictionary TryGetValue or ContainsKey+Item?
一般情况下,正如评论中所指出的那样,除非您在分析器下使用真实数据运行它,否则您不知道程序中的性能热点。
此外,通常,由于可能存在副作用,命令式语言中的编译器无法优化重复调用。每次调用函数都可以返回一个新值。顺便说一句,这也是你自己不应该依赖重复呼叫的原因之一。今天也许你知道函数或属性是无副作用的,重复调用返回相同的值,但明天你改变它并在里面添加一些随机生成器,所有重复调用的逻辑都将被破坏。