同一语句中对同一个方法进行多次调用,编译器会进行优化吗?

3

假设我在Java 8中(特别是Android)执行以下操作:

String name = someObject.getName() != null ? someObject.getName() : "null";

假设方法getName()可能会有许多调用其他方法来解析名称。此外,假设我经常调用这段代码。

从性能的角度考虑,像这样做是否更好呢?

String name = someObject.getName();
name = name != null ? name : "null";

1
很有可能该方法在不同的调用时返回不同的结果,因此编译器可能无法对其进行优化。 - Arnaud
5
在Java 9中,可以使用Objects.requireNonNullElse(someObject.getName(), "null")来处理所有类型的对象,这是一种可用的变体。因此,建议使用String name = Objects.requireNonNullElse(someObject.getName(), "null");而不是String name = String.valueOf(someObject.getName());。请注意,两种写法的意思相同,只是前者更加通用。 - Holger
2个回答

5
这似乎是一个可能的优化,称为CSE;据我所知,JVM可以做到这一点(但不确定Android是否可以)。
但这在很大程度上取决于getName的作用,如果它在内部分配其他对象并执行其他操作,则情况可能不同。不幸的是,我甚至无法告诉自己如何证明我在这里是错误还是正确的(可能需要调查);老实说,我有一个自己做这件事的习惯。例如:
for(int i=0;i<list.size();++i){

}

我尝试在循环之前提取int size = list.size(),即使在我的理解中这可能受到标量替换优化的影响。

2
在所有单独调用被内联之后,CSE将应用。然后,优化器能够看到代码是否无副作用并且每次都归结为相同的操作。关于循环中的list.size(),它可能会被优化为重复调用相同方法的方式,但是,如果循环体包含停止优化器的操作,例如影响跨线程可见性的操作,则需要一个可证明的纯本地列表实例才能仍然具有该优化。否则,它可能会最终导致代码每次重新读取size字段。 - Holger
@Holger,将这两个语句合并到一个变量中,这样做是个好主意,对吧? - Eugene
7
为了清晰明了,你应该始终避免代码重复。像someObject.getName() != null ? someObject.getName() : "null"这样的表达式,读者并不清楚someObject.getName()每次是否真的求值得到相同的结果(尽管作者假定是这样的)。这不仅取决于getName()方法的实现方式,还取决于someObject变量的性质,可能是一个可变的共享变量。但请考虑[这些替代方案](https://dev59.com/367la4cB1Zd3GeqPgqMP#2LajEYcBWogLw_1bJTQU)... - Holger
1
@Holger 哈!有趣的事情:我今天在一次面试中才了解到这个,应该记住的。非常好的观点! - Eugene

0

第二个版本只调用了一次getName,而第一个版本在不为空的情况下会调用两次...所以如果它很重,第二个版本更好。

正如Arnaud所说,您必须考虑值是否可以在两次调用之间更改:在这种情况下,您将获得性能提升,但有风险获得“旧”值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接