你好,我有两个代码示例
if / else if / else语句
private Object getObj(message) {
if (message.getA() != null)
return message.getA();
else if (message.getB() != null)
return message.getB();
else if (message.getC() != null)
return message.getC();
else return null;
}
可选语句
private Optional<Object> wrap(Object o){
return Optional.ofNullable(o);
}
private Object getObj(message) {
return wrap(message.getA())
.orElseGet(() -> wrap(message.getB())
.orElseGet(() -> wrap(message.getC())
.orElse(null)));
}
所以我的问题是,这两者在性能方面如何比较(我在实际代码中大约有15-20条if-else语句)?
值得重构代码的可读性与性能,还是滥用可选选项?
如果if / else-if语句增长到100+,那么性能损失会是多少?
预先感谢
答案 0 :(得分:14)
请勿将Optional
用作条件逻辑。
它们是to be returned from a method to indicate a potentially absent value设计的。
仅仅因为您可以将它们很好地链接成一行并不意味着它是可以理解的。您实际上也一无所获。性能开销可能很大。最坏的情况是先创建N
对象,然后将其丢弃。只需呆在您的“普通” if-else
链上即可。
与其寻找让您的当前代码更具可读性的方法,不如退一步,问问自己为什么,您需要15-20条if-else语句。你能分解一些逻辑吗?为什么首先需要为具有多个可能具有不同类型的多个不同字段使用一个吸气剂?等等
答案 1 :(得分:5)
还有第三种形式(允许一些变化)。
return Stream.<Supplier<Object>>of(message::getA, message::getB, message::getC)
.map(Supplier::get)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
目前可能是最不灵活和效率最高的,但是很清楚。
答案 2 :(得分:3)
如果您的目标是压缩代码,请使用三元链接。性能可能与一系列if-then-else语句的性能相同。
( this.getA() != null ) ? this.getA()
: ( this.getB() != null ) ? this.getB()
: ( this.getC() != null ) ? this.getC()
: null;
正如Answer by Lino正确指出的那样,您正在尝试使Optional
超出其原始设计目的(在lambda和stream中返回值)。通常最好仅将Optional
与return
语句一起使用,只有在您想弄清楚null是要返回的有效值时,才这样。参见Brian Goetz的this Answer。
ternary operator是浓缩的if-then-else
,结合成一个单线。
result = test ? valueToUseIfTestIsTrue : valueToUseIfTestIsFalse
示例:
Color color = isPrinterMonochrome ? Color.GREY : Color.GREEN ;
使用三元语句链。
所以这个:
if ( this.getA() != null )
return this.getA();
else if ( this.getB() != null )
return this.getB();
else if ( this.getC() != null )
return this.getC();
else return null;
…成为这个:
return
( this.getA() != null ) ? this.getA()
: ( this.getB() != null ) ? this.getB()
: ( this.getC() != null ) ? this.getC()
: null;
示例代码。
public String getA ()
{
// return "A";
return null;
}
public String getB ()
{
// return "B";
return null;
}
public String getC ()
{
return "C";
// return null;
}
public String getABC ()
{
if ( this.getA() != null )
return this.getA();
else if ( this.getB() != null )
return this.getB();
else if ( this.getC() != null )
return this.getC();
else return null;
}
public String getABCTernary ()
{
return
( this.getA() != null ) ? this.getA()
: ( this.getB() != null ) ? this.getB()
: ( this.getC() != null ) ? this.getC()
: null;
}
运行该示例代码。
String s = this.getABCTernary();
System.out.println( "s: " + s );
C
这两个在性能方面的比较
Java中的三元运算符是"short-circuiting",这意味着与测试结果匹配的左侧或右侧是唯一调用的代码。在此处的代码中,如果getA
返回一个非null值,则立即返回该值。从未执行对getB
和getC
的进一步调用。因此,就此而言,链式三元组的性能与级联的if-then-else语句相同:先匹配胜出,无需进一步调用。
如果您将性能表示为执行的纳秒级,我不知道。担心这一点将落入过早优化的陷阱。现代JVM在优化代码方面经过了很好的调整。
答案 3 :(得分:2)
几天前,我进行了彻底的性能分析。这会对性能产生巨大影响。使用AdoptOpenJDK,if
语句的速度提高了10倍。当JIT编译器热运行时,这将减少20%的罚款。
GraalVM做得更好:冷的JVM的速度降低了3倍,并且在给编译器足够的时间执行其魔术之后,还会有20%的性能损失。
但是,真正的问题是哪个版本更适合阅读和维护应用程序。如果您像我一样,则更容易阅读if
语句,但也有人喜欢函数式方法。
如果您准备进行深入的探讨,我邀请您read my detailed analysis来介绍Optional.orElseGet()
及其朋友的性能和实现。
答案 4 :(得分:1)
在经过20年的商业经验之后,我认为追求可读性是绝对的愚蠢,同时故意编写复杂的代码是邪恶的。
我知道这完全违背了民意。
但是,每个人都需要意识到这一点...
if
或Optional
是否更具可读性,我们也有不同的看法。无论我们所处的构造或情况如何,都会发生这类辩论。if
选项,该选项比功能方法的性能更高,每一次 和 ,那么阅读该代码的人就会得到习惯了并找到 MORE READABLE -因为这是他们现在已经习惯的样式。因此,从本质上讲:与if
一起使用...不要不要使用该Optional
!
答案 5 :(得分:0)
我同意不过度使用Optional的方法,它们变得混乱不堪,就像嵌套流在多维数组上进行迭代一样。
但是,出于好奇,我创建了以下应用程序来计算Optional
超过If/Else
的性能命中率。它始终使我的机器运行时间少于10毫秒/百万。如果您想使用它,可以使用以下代码,并可以将其用于类似的比较:
import java.util.Optional;
import java.util.concurrent.Callable;
public class OptionalVsIfElse {
public static long time(double numberOfRuns, Callable<Object> tested) throws Exception {
long start = System.currentTimeMillis();
for (long idx = 0; idx < numberOfRuns; idx++)
tested.call();
return System.currentTimeMillis() - start;
}
public static void main(String[] args) throws Exception {
double numberOfRuns = 10000000;
Object target = new Object();
Object comparison = new Object();
long ifTime = time(numberOfRuns, () -> {
return (target != null) ? target : comparison;
});
long optionalTime = time(numberOfRuns, () -> {
return Optional.ofNullable(target).orElse(comparison);
});
double performanceHitRatio = (double)(optionalTime - ifTime) * 1_000_000 / numberOfRuns;
System.out.println(String.format("Performance Hit Ratio: %.2f ms / million runs", performanceHitRatio));
}
}