可选的`orElse`懒惰评估失败会导致性能下降

时间:2017-02-08 08:50:34

标签: java optional

考虑具有用户首选项系统的应用程序的以下用例:

我们希望得到偏好MyFlag的bool值 在最好的情况下,我们希望它来自当前用户的设置 如果失败,我们希望从默认设置中获取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
}

这里的问题是连接速度很慢。调用.orElse(slowlyGet...);时,首先评估func slowlyGetDefaultPreference(),无论是否我的可选项为空或具有值。这是性能损失,我必须避免。

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

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

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));
    }
}

同样适用于抛出异常,因为我不想支付构建异常的费用。

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

2 个答案:

答案 0 :(得分:3)

当我测试它时,它只打印"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()));
    }
}

答案 1 :(得分:3)

orElse()orElseGet(lambdas)之间的区别在于,第一个函数始终被调用,但是orElseGet被调用,而orElseGetOptional.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));

然后,当map函数中的所有对象都不是null

时,您的代码不会评估最后一个方法