Optional 的 `orElse` 惰性求值失败导致性能下降

Da *_*enk 6 java optional

考虑具有用户偏好系统的应用程序的以下用例:

我们想要获得偏好的布尔值MyFlag
在最好的情况下,我们希望它来自当前用户的设置。
如果失败,我们希望MyFlag从默认设置中获取。
如果连那都失败了,扔掉。

设置在服务器上。此连接速度很慢并且可能会失败。
获取设置和获取首选项也可能会失败。
所以让我们使用 javas Optionals:

public static boolean getMyFlag()throws NoSuchElementException
{
        return getUserOrDefaultPreference("MY_FLAG");
}

private Boolean getUserOrDefaultPreference(String preferenceName) throws NoSuchElementException
{
    return Optional.ofNullable(getConnection())              // get slow connection
        .map(connection -> connection.getUserSettings())     // get user settings
        .map(settings -> settings.getPref(preferenceName))   // return preference
        .orElse(slowlyGetDefaultPreference(preferenceName)); // or else return default value
}

private Boolean slowlyGetDefaultPreference(String preferenceName) throws NoSuchElementException
{
    return Optional.ofNullable(getConnection())             // get slow connection
        .map(connection -> connection.getDefaultSettings()) // get default settings
        .map(settings -> settings.getPref(preferenceName))  // return preference
        .orElseThrow(() ->  new NoSuchElementException());  // if any of the above fails throw
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是连接可能很慢。当.orElse(slowlyGet...);被调用时,slowlyGetDefaultPreference()首先评估func ,无论我的 optional 是空的还是有值。这是性能损失,我必须避免。

我尝试通过 使用供应商.orElseGet(() -> slowlytGet..),但这导致了同样的问题。

所以我唯一的办法是丑陋的isPresent()反模式,从可读性的角度来看,它破坏了整个 Optional 流程:

private Boolean getUserOrDefaultPreference(String preferenceName) throws NoSuchElementException
{
    Optional<Boolean> opt = Optional.ofNullable(getConnection())
        .map(connection -> connection.getUserSettings())  
        .map(settings -> settings.getPref(preferenceName));

    if(opt.isPresent())
    {
        return opt.get();
    }
    else
    {
        slowlyGetDefaultPreference(preferenceName));
    }
}
Run Code Online (Sandbox Code Playgroud)

抛出异常也是如此,因为我不想支付构建异常的成本。

我在这里遗漏了什么还是这是唯一的解决方案?

Mat*_*W90 6

之间的区别orElse()orElseGet(lambdas)是第一个函数总是被调用,但orElseGet被调用,当之前的一些obejctorElseGetOptional.ofNullable()为空。

这意味着,如果您不想每次都从 调用方法orElse(),则必须使用orElseGet()

在您的示例中,对于您的方法,slowlyGetDefaultPreference(preferenceName)请使用orElseGet()

return Optional.ofNullable(getConnection())              // get slow connection
    .map(connection -> connection.getUserSettings())     // get user settings
    .map(settings -> settings.getPref(preferenceName))   // return preference
    .orElseGet(() -> slowlyGetDefaultPreference(preferenceName));
Run Code Online (Sandbox Code Playgroud)

然后你编码不评估最后一个方法,当 map 函数中的所有对象都不是 null


Pat*_*ker 5

当我测试它时,它只打印"smart"

public class SampleJava {
    public static String stupid() {
      System.out.println("stupid");
      return "stupid";
    }
    public static void main(String[] args) {
      System.out.println(Optional.ofNullable("smart").orElseGet(() -> stupid()));
    }
}
Run Code Online (Sandbox Code Playgroud)