可选 vs if/else-if 性能 java 8

Optional vs if/else-if performance java 8

你好,我有两个代码示例

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+,性能损失是多少?

提前致谢

不要将 Optional 用于条件逻辑。

它们是设计出来的,to be returned from a method to indicate a potentially absent value

仅仅因为您可以很好地将它们链接成一行并不意味着它是可以理解的。而且你实际上一无所获。性能开销 可能 很大。在最坏的情况下 N 个对象被创建然后被丢弃。留在你的 "normal" if-else 链条上。


与其想方设法让您当前的代码更具可读性,不如退后一步问问自己 为什么 您需要 15-20 if-else 条语句。你能分解一些逻辑吗?为什么首先需要 getter 用于这么多可能具有不同类型的不同字段?等等

还有第三种形式(仍然允许一些变化)。

return Stream.<Supplier<Object>>of(message::getA, message::getB, message::getC)
        .map(Supplier::get)
        .filter(Objects::nonNull)
        .findFirst()
        .orElse(null);

目前可能是最不灵活和效率最低的,但很清楚。

tl;博士

如果您的目标是压缩代码,则使用三元链接。性能可能与一系列 if-then-else 语句的性能相同。

        ( this.getA() != null ) ? this.getA()
                : ( this.getB() != null ) ? this.getB()
                : ( this.getC() != null ) ? this.getC()
                : null;

三元链

作为 Brian Goetz 的 correctly states, you are trying to take Optional beyond their original design purpose (returning values within lambdas & streams). Generally best to use Optional only with a return statement, and only then when you want to make clear that null is a valid value to be returned. See this Answer

一个ternary operator是一个压缩的if-then-else,合并成一个one-liner.

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

优缺点

  • 三元链的优势是压缩代码,折叠成 one-liner。
  • 缺点是您在这种特定情况下两次调用 getter 方法只是为了获得一个值。对于简单的 fetch-the-variable 类型的 getter 不是问题,但如果 getter 是 time-consuming 方法(例如远程 Web 服务调用),则会影响性能。而且,级联if-then-else也有同样的问题,也调用了你的getter两次。

性能

how these two compare in terms of performance

Java中的三元运算符是"short-circuiting",意思是匹配测试结果的左侧或右侧是唯一调用的代码。在我们这里的代码中,如果 getA returns 一个 non-null 值,则立即返回该值。对 getBgetC 的进一步调用永远不会执行。所以在这方面,链式三元的表现和级联if-then-else语句一样:first-match wins, no further calls.

如果您指的是执行纳秒级的性能,我不知道。担心这一点会落入 过早优化 的陷阱。现代 JVM 非常适合 well-tuned 优化您的代码。

几天前我运行 进行了彻底的性能分析。有巨大的性能影响。使用 AdoptOpenJDK,if 语句最多可快 10 倍。当 JIT 编译器热运行时,这会减少 20% 的惩罚。

GraalVM 做得更好:冷 JVM 的速度降低了 3 倍,并且在给编译器足够的时间来发挥它的魔力之后,还有 20% 的性能损失。

然而,真正的问题是哪个版本更适合阅读和维护应用程序。如果您像我一样,阅读 if 语句会更容易,但也有人更喜欢函数式方法。

如果您准备好进行深入研究,我邀请您read my detailed analysis了解 Optional.orElseGet() 及其朋友的性能和实施。

我认为经过近20年的商业经验,我认为追求可读性是绝对愚蠢的,同时故意编写复杂的代码是邪恶的。

我知道这完全违背了大众的看法。

然而,每个人都需要意识到这一点...

  1. 一个人可读的内容不一定对另一个人可读。即使在这个线程中,我们对 ifOptional 是否更具可读性也有不同的看法。无论我们所处的构造或情况如何,这些类型的辩论都会发生。
  2. 如果我们采用 if 选项,它比函数式方法更高效,eachevery time,那么阅读该代码的人就会习惯它并发现它 更具可读性 - 因为这是他们现在已经习惯的风格。
  3. 高性能代码不必“难以阅读”...但这会循环回到第 1 点和第 2 点。开发人员需要真正了解他们正在使用的语言的基础知识,并编写适合该语言的代码,而不是试图在他们的代码中形成“英语句子”。

所以,本质上:使用 if... 而不是 使用 Optional!