`Optional.orElse()`和`Optional.orElseGet()`之间的区别

jbx*_*jbx 183 java optional java-8

我试图理解Optional<T>.orElse()Optional<T>.orElseGet()方法之间的区别.

orElse()方法的描述是"如果存在则返回值,否则返回其他值".

虽然,该orElseGet()方法的描述是"如果存在返回值,否则调用other并返回该调用的结果".

orElseGet()方法采用供应商功能接口,基本上不接受任何参数和返回T.

你需要在哪种情况下使用orElseGet()?如果你有一个方法,你T myDefault()为什么不这样做optional.orElse(myDefault())而不是optional.orElseGet(() -> myDefault())

似乎没有orElseGet()将lambda表达式的执行推迟到以后的某个时间或什么的,所以有什么意义呢?(我本以为如果它返回一个更安全Optional<T>的东西会更有用,它get()永远不会抛出一个NoSuchElementException并且isPresent()总是返回真实......但显然它不是,它只是返回T就像orElse()).

我还缺少其他一些差异吗?

biz*_*lop 154

采取以下两种情况:

Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );
Run Code Online (Sandbox Code Playgroud)

如果opt不包含值,则两者确实等价.但如果opt 确实包含一个值,那么Foo将创建多少个对象?

Ps:当然在这个例子中差异可能是不可测量的,但是如果你必须从远程Web服务或数据库中获取默认值,它突然变得非常重要.

  • 谢谢你们的澄清.所以差异很微妙但很重要.在第二种情况下,它不会创建一个新的`Foo`对象,而在第一种情况下它将创建它,但如果在`Optional`中有一个值,则不使用它. (17认同)
  • 这是我第一次看到问题而不是答案被接受. (5认同)
  • @jbx是的,在我的例子中它可以说没有任何实际区别,但是如果您必须从远程Web服务或数据库中获取默认值,则差异突然变得非常重要. (4认同)
  • "*如果您必须从远程Web服务获取默认值,例如*"这完全是我的方案.在我的例子中,可选项是一个查询,缺省查询时的默认值是获取所有值...是的,或者ElEGet将该操作的运行时间减少了1000倍. (3认同)
  • @jbx:你正在混合两件事.关于奇怪的基准测试结果已经存在问题,这些结果仅仅是因为没有使用计算结果.JVM*可以做到这一点.另一方面,`System.out.println()`不是*计算,而是产生可观察副作用的语句.我已经说过,可观察的副作用会阻碍优化(控制台输出流*是一个外部资源). (2认同)
  • 是否有任何场景应该使用 orElse 而不是 orElseGet? (2认同)
  • @lapkritinis 我想不出任何情况下 `orElse()` 会给你带来不同的结果,除非你计划使用一个非有效的最终局部变量的值,`orElseGet()` 根本不会编译。当与常量或变量一起使用时,我总是使用“orElse()”,因为“orElse(42)”比“orElseGet(() -&gt; 42)”更容易阅读 (2认同)

nxh*_*oaf 89

简答:

  • 无论Optional.isPresent()值是多少,orElse()总是会调用给定的函数,无论你是否需要它
  • orElseGet()只会在调用给定函数时调用Optional.isPresent() == false

在实际代码中,当需要的资源很昂贵时,您可能需要考虑第二种方法.

// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource()); 

// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource()) 
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请考虑以下使用此函数的示例:

public Optional<String> findMyPhone(int phoneId)
Run Code Online (Sandbox Code Playgroud)

区别如下:

                           X : buyNewExpensivePhone() called

+——————————————————————————————————————————————————————————————————+——————————————+
|           Optional.isPresent()                                   | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone())          |   X  |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) |      |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+
Run Code Online (Sandbox Code Playgroud)

什么时候optional.isPresent() == false,两种方式没有区别.但是,当您无论是否需要时optional.isPresent() == true,orElse()始终调用后续函数.

最后,使用的测试用例如下:

结果:

------------- Scenario 1 - orElse() --------------------
  1.1. Optional.isPresent() == true
    Going to a very far store to buy a new expensive phone
    Used phone: MyCheapPhone

  1.2. Optional.isPresent() == false
    Going to a very far store to buy a new expensive phone
    Used phone: NewExpensivePhone

------------- Scenario 2 - orElseGet() --------------------
  2.1. Optional.isPresent() == true
    Used phone: MyCheapPhone

  2.2. Optional.isPresent() == false
    Going to a very far store to buy a new expensive phone
    Used phone: NewExpensivePhone
Run Code Online (Sandbox Code Playgroud)

码:

public class TestOptional {
    public Optional<String> findMyPhone(int phoneId) {
        return phoneId == 10
                ? Optional.of("MyCheapPhone")
                : Optional.empty();
    }

    public String buyNewExpensivePhone() {
        System.out.println("\tGoing to a very far store to buy a new expensive phone");
        return "NewExpensivePhone";
    }


    public static void main(String[] args) {
        TestOptional test = new TestOptional();
        String phone;
        System.out.println("------------- Scenario 1 - orElse() --------------------");
        System.out.println("  1.1. Optional.isPresent() == true");
        phone = test.findMyPhone(10).orElse(test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("  1.2. Optional.isPresent() == false");
        phone = test.findMyPhone(-1).orElse(test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("------------- Scenario 2 - orElseGet() --------------------");
        System.out.println("  2.1. Optional.isPresent() == true");
        // Can be written as test::buyNewExpensivePhone
        phone = test.findMyPhone(10).orElseGet(() -> test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("  2.2. Optional.isPresent() == false");
        phone = test.findMyPhone(-1).orElseGet(() -> test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的例子 - 但我真的不明白“Optional.orElse”的Javadocs如何表明“如果存在值,则返回该值,否则返回其他”可以暗示这种行为...... (2认同)

Jin*_*won 60

我到达这里是为了Kudo提到的问题.

我正在为别人分享我的经验.

orElse或者orElseGet,这就是问题:

static String B() {
    System.out.println("B()...");
    return "B";
}

public static void main(final String... args) {
    System.out.println(Optional.of("A").orElse(B()));
    System.out.println(Optional.of("A").orElseGet(() -> B()));
}
Run Code Online (Sandbox Code Playgroud)

版画

B()...
A
A
Run Code Online (Sandbox Code Playgroud)

  • 这里的问题实际上是方法的命名.`or`前缀误导开发人员(包括我在问我问题时)认为这是一个短路操作,因为这是我们在布尔条件下习惯的.但是,它不是,它只是一个在其前缀中带有"或"的方法名称,因此它的参数将被评估,而不管"Optional"是否携带值.令人遗憾的是,命名令人困惑,而不是我们可以对此做任何事情. (7认同)
  • 这不成问题'.简单的事实是在方法执行之前评估方法的参数.如果将`B()`传递给名为`orElse()`或`abc()`的方法,它就没有任何区别,`B()`得到了评估. (6认同)
  • 感谢你的回答。必须说语言设计者没有考虑到这会变得多么混乱。 (2认同)

dev*_*ang 35

我想说,当我们想要评估某些东西以获得条件中的新值时,orElse和之间的最大区别.orElseGetelse

考虑这个简单的例子 -

// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
    value = oldValue;
} else {
    value = apicall().value;
}
Run Code Online (Sandbox Code Playgroud)

现在,让我们改变上面的例子中,使用OptionalorElse,

// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);
Run Code Online (Sandbox Code Playgroud)

现在,让我们改变上面的例子中,使用OptionalorElseGet,

// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);
Run Code Online (Sandbox Code Playgroud)

orElse被调用时,apicall().value被评估并传递给该方法.然而,在orElseGet评估的情况下,只有在oldValue空的情况下才会发生.orElseGet允许懒惰评估.

  • 由于ifElse()的这种"奇怪"行为,我浪费了很多次.我想说ifElseGet()优于ifElse()是有道理的 (3认同)

Piy*_*h N 7

首先检查这两个方法的声明。

\n

1) OrElse:执行逻辑并将结果作为参数传递。

\n
public T orElse(T other) {    \n return value != null ? value : other;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

2) OrElseGet:如果可选值内的值为空则执行逻辑

\n
public T orElseGet(Supplier<? extends T> other) {\n  return value != null ? value : other.get(); \n}\n
Run Code Online (Sandbox Code Playgroud)\n

对上述声明的一些解释: \n\xe2\x80\x9cOptional.orElse\xe2\x80\x9d 的参数始终被执行,无论可选对象的值如何(null、空或有值)。使用 \xe2\x80\x9cOptional.orElse\xe2\x80\x9d 时请务必考虑上述几点,否则在以下情况下使用 \xe2\x80\x9cOptional.orElse\xe2\x80\x9d 可能会非常危险情况。

\n

风险-1) 日志记录问题:如果 orElse 中的内容包含任何日志语句:\n在这种情况下,您每次都会记录它。

\n
Optional.of(getModel())\n   .map(x -> {\n      //some logic\n   })\n  .orElse(getDefaultAndLogError());\n \ngetDefaultAndLogError() {\n  log.error("No Data found, Returning default");\n  return defaultValue;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

风险-2) 性能问题:如果 orElse 中的内容是时间密集型的:\n时间密集型内容可以是任何 I/O 操作 DB 调用、API 调用、文件读取。如果我们将这样的内容放入 orElse() 中,系统最终将执行无用的代码。

\n
Optional.of(getModel())\n   .map(x -> //some logic)\n   .orElse(getDefaultFromDb());\n\ngetDefaultFromDb() {\n   return dataBaseServe.getDefaultValue(); //api call, db call.\n}\n
Run Code Online (Sandbox Code Playgroud)\n

风险 3) 非法状态或错误问题:如果 orElse 中的内容正在改变某些对象状态:\n我们可能在另一个地方使用同一个对象,比如在Optional.map 函数中,它可能会让我们陷入严重错误。

\n
List<Model> list = new ArrayList<>();\nOptional.of(getModel())\n  .map(x -> {\n  })\n  .orElse(get(list));\n\nget(List < String > list) {\n   log.error("No Data found, Returning default");\n   list.add(defaultValue);\n   return defaultValue;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

那么,我们什么时候可以使用 orElse() 呢?\n当默认值是某个常量对象(枚举)时,首选使用 orElse。在上述所有情况下,我们可以使用Optional.orElseGet()(仅当Optional包含空值时执行)而不是Optional.orElse()。为什么??在orElse中,我们传递默认结果值,但在orElseGet中,我们传递Supplier,并且Supplier的方法仅在Optional中的值为null时执行。

\n

从中得出的主要结论是:

\n
    \n
  1. 如果 \xe2\x80\x9cOptional.orElse\xe2\x80\x9d 包含任何日志语句,请勿使用它。
  2. \n
  3. 如果包含时间密集型逻辑,请勿使用 \xe2\x80\x9cOptional.orElse\xe2\x80\x9d。
  4. \n
  5. 如果 \xe2\x80\x9cOptional.orElse\xe2\x80\x9d 正在改变某些对象状态,请勿使用它。
  6. \n
  7. 如果我们必须返回常量枚举,请使用 \xe2\x80\x9cOptional.orElse\xe2\x80\x9d。
  8. \n
  9. 在第 1,2 点和第 3 点提到的情况下,首选 \xe2\x80\x9cOptional.orElseGet\xe2\x80\x9d 。
  10. \n
\n

我已经在 point-2 ( \xe2\x80\x9cOptional.map/Optional.orElse\xe2\x80\x9d != \xe2\x80\x9cif/else\xe2\x80\x9d ) 我的中等博客中解释了这一点。作为程序员而不是编码员使用 Java8

\n


Vis*_*tna 5

差异非常微妙,如果您不加注意,就会以错误的方式使用它。

\n\n

orElse()理解和之间区别的最佳方法orElseGet()是,无论是否nullorElse()将始终执行,但仅在为null时才会执行。Optional<T>orElseGet()Optional<T>

\n\n

orElse的字典含义是:-当某些东西不存在时执行该部分,但这里它矛盾,请参见下面的示例:

\n\n
    Optional<String> nonEmptyOptional = Optional.of("Vishwa Ratna");\n    String value = nonEmptyOptional.orElse(iAmStillExecuted());\n\n    public static String iAmStillExecuted(){\n    System.out.println("nonEmptyOptional is not NULL,still I am being executed");\n    return "I got executed";\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

输出: nonEmptyOptional 不为 NULL,但我仍然被执行

\n
\n\n
\n\n
    Optional<String> emptyOptional = Optional.ofNullable(null);\n    String value = emptyOptional.orElse(iAmStillExecuted());\n    public static String iAmStillExecuted(){\n    System.out.println("emptyOptional is NULL, I am being executed, it is normal as \n    per dictionary");\n    return "I got executed";\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

输出:emptyOptional为NULL,我正在被执行,根据\n字典这是正常的

\n\n

对于orElseGet(),该方法按照字典的含义, 只有当Optional为\n nullorElseGet()时才会执行\n部分 。

\n
\n\n

基准

\n\n
+--------------------+------+-----+------------+-------------+-------+\n| Benchmark          | Mode | Cnt | Score      | Error       | Units |\n+--------------------+------+-----+------------+-------------+-------+\n| orElseBenchmark    | avgt | 20  | 60934.425  | \xc2\xb1 15115.599 | ns/op |\n+--------------------+------+-----+------------+-------------+-------+\n| orElseGetBenchmark | avgt | 20  | 3.798      | \xc2\xb1 0.030     | ns/op |\n+--------------------+------+-----+------------+-------------+-------+\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

备注: 对于我们的特定示例来说,orElseGet()其表现明显优于。orElse()

\n
\n\n

希望它能消除像我这样想要最基本的示例的人的疑虑:)

\n