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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33170109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:24:33  来源:igfitidea点击:

Difference between `Optional.orElse()` and `Optional.orElseGet()`

javajava-8optional

提问by jbx

I am trying to understand the difference between the Optional<T>.orElse()and Optional<T>.orElseGet()methods.

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

The description for the orElse()method is "Return the value if present, otherwise return other."

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

While, the description for the orElseGet()method is "Return the value if present, otherwise invoke other and return the result of that invocation."

而该orElseGet()方法的描述是“如果存在返回值,否则调用其他并返回该调用的结果”。

The orElseGet()method takes a Supplier functional interface, which essentially does not take any parameters and returns T.

orElseGet()方法采用 Supplier 功能接口,该接口本质上不采用任何参数并返回T

In which situation would you need to use orElseGet()? If you have a method T myDefault()why wouldn't you just do optional.orElse(myDefault())rather than optional.orElseGet(() -> myDefault())?

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

It does not seem that orElseGet()is postponing the execution of the lambda expression to some later time or something, so what's the point of it? (I would have thought that it would be more useful if it returned a safer Optional<T>whose get()never throws a NoSuchElementExceptionand isPresent()always returns true... but evidently its not, it just returns Tlike orElse()).

这似乎不是orElseGet()将 lambda 表达式的执行推迟到稍后的时间或其他什么,那有什么意义呢?(我会认为如果它返回一个更安全的,Optional<T>get()从不抛出 aNoSuchElementException并且isPresent()总是返回 true会更有用......但显然它不是,它只是返回TorElse())。

Is there some other difference I am missing?

我还缺少其他一些区别吗?

采纳答案by biziclop

Take these two scenarios:

以这两个场景为例:

Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );

If optdoesn't contain a value, the two are indeed equivalent. But if optdoescontain a value, how many Fooobjects will be created?

如果opt不包含值,则两者确实等效。但是如果opt确实包含一个值,Foo将创建多少个对象?

P.s.: of course in this example the difference probably wouldn't be measurable, but if you have to obtain your default value from a remote web service for example, or from a database, it suddenly becomes very important.

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

回答by Jin Kwon

I reached here for the problem Kudomentioned.

我到达这里是为了解决工藤提到的问题。

I'm sharing my experience for others.

我正在为其他人分享我的经验。

orElse, or orElseGet, that is the question:

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

prints

印刷

B()...
A
A

orElseevaluates the value of B() interdependently of the value of the optional. Thus, orElseGetis lazy.

orElse评估 B() 的值与可选的值相互依赖。因此,orElseGet是懒惰的。

回答by devang

I would say the biggest difference between orElseand orElseGetcomes when we want to evaluate something to get the new value in the elsecondition.

我会说orElse和之间的最大区别orElseGet是当我们想要评估某些东西以获得else条件中的新值时。

Consider this simple example -

考虑这个简单的例子——

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

Now let's transform the above example to using Optionalalong with orElse,

现在让我们将上面的示例转换为使用Optionalwith orElse

// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);

Now let's transform the above example to using Optionalalong with orElseGet,

现在让我们将上面的示例转换为使用Optionalwith orElseGet

// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);

When orElseis invoked, the apicall().valueis evaluated and passed to the method. Whereas, in the case of orElseGetthe evaluation only happens if the oldValueis empty. orElseGetallows lazy evaluation.

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

回答by nxhoaf

Short Answer:

简答:

  • orElse()will always call the given function whether you want it or not, regardless of Optional.isPresent()value
  • orElseGet()will only call the given function when the Optional.isPresent() == false
  • orElse()将始终调用给定的函数,无论您是否需要,无论Optional.isPresent()值如何
  • orElseGet()只会在Optional.isPresent() == false

In real code, you might want to consider the second approach when the required resource is expensive to get.

在实际代码中,当所需资源的获取成本很高时,您可能需要考虑第二种方法。

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

// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource()) 

For more details, consider the following example with this function:

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

public Optional<String> findMyPhone(int phoneId)

The difference is as below:

区别如下:

                           X : buyNewExpensivePhone() called

+——————————————————————————————————————————————————————————————————+——————————————+
|           Optional.isPresent()                                   | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone())          |   X  |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) |      |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+

When optional.isPresent() == false, there is no difference between two ways. However, when optional.isPresent() == true, orElse()always calls the subsequent function whether you want it or not.

当 时optional.isPresent() == false,两种方式没有区别。然而,当optional.isPresent() == trueorElse()总是叫你是否希望与否的后续功能。

Finally, test case used is as below:

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

Result:

结果:

------------- 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

Code:

代码:

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

回答by ratzip

Considering the following code:

考虑以下代码:

import java.util.Optional;

// one class needs to have a main() method
public class Test
{
  public String orelesMethod() {
    System.out.println("in the Method");
    return "hello";
  }

  public void test() {
    String value;
    value = Optional.<String>ofNullable("test").orElseGet(this::orelesMethod);
    System.out.println(value); 

    value = Optional.<String>ofNullable("test").orElse(orelesMethod());
    System.out.println(value); 
  }

  // arguments are passed using the text field below this editor
  public static void main(String[] args)
  {
    Test test = new Test();

    test.test();
  }
}

if we get valuein this way: Optional.<String>ofNullable(null), there is no difference between orElseGet() and orElse(), but if we get valuein this way: Optional.<String>ofNullable("test"), orelesMethod()in orElseGet()will not be called but in orElse()it will be called

如果我们value以这样的方式Optional.<String>ofNullable(null),有orElseGet()和否则容易()没有什么区别,但如果我们value以这样的方式Optional.<String>ofNullable("test")orelesMethod()orElseGet()不会被调用,但orElse()它会被称为

回答by Maroun

The following example should demonstrate the difference:

下面的例子应该展示了不同之处:

String destroyTheWorld() {
  // destroy the world logic
  return "successfully destroyed the world";
}

Optional<String> opt = Optional.empty();

// we're dead
opt.orElse(destroyTheWorld());

// we're safe    
opt.orElseGet(() -> destroyTheWorld());

The answer appears in the docs as well.

答案也出现在文档中。

public T orElseGet(Supplier<? extends T> other):

public T orElseGet(Supplier<? extends T> other)

Return the value if present, otherwise invokeother and return the result of that invocation.

返回值(如果存在),否则调用other 并返回该调用的结果。

The Supplierwon'tbe invoked if the Optionalpresents. whereas,

如果存在,Supplier则不会调用Optional。然而,

public T orElse(T other):

public T orElse(T other)

Return the value if present, otherwise return other.

如果存在则返回值,否则返回其他值。

If otheris a method that returns a string, it will be invoked, but it's value won't be returned in case the Optionalexists.

如果other是一个返回字符串的方法,它将被调用,但如果Optional存在,则不会返回它的值。

回答by Vishwa Ratna

The difference is pretty subtle and if you dont pay much attention then you will keep it using in a wrong way.

差异非常微妙,如果您不注意,那么您将继续以错误的方式使用它。

Best way to understand the difference between orElse()and orElseGet()is that orElse()will always be executed if the Optional<T>is nullor not, But orElseGet()will only be executed when Optional<T>is null.

最好的方式来理解之间的区别orElse(),并orElseGet()是,orElse()如果总是被执行Optional<T>不是,但orElseGet()何时才会执行Optional<T>

The dictionary meaning of orElseis :-execute the part when something is not present, but here it contradicts, see the below example:

orElse的字典含义是:-当某物不存在时执行该部分,但在这里它是矛盾的,见下面的例子:

    Optional<String> nonEmptyOptional = Optional.of("Vishwa Ratna");
    String value = nonEmptyOptional.orElse(iAmStillExecuted());

    public static String iAmStillExecuted(){
    System.out.println("nonEmptyOptional is not NULL,still I am being executed");
    return "I got executed";
    }

Output:nonEmptyOptional is not NULL,still I am being executed

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



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

Output: emptyOptional is NULL, I am being executed, it is normal as per dictionary

For orElseGet(), The method goes as per dictionary meaning, The orElseGet()part will be executed only when the Optional is null.

输出:emptyOptional 为NULL,我正在被执行,按照字典是正常的

对于orElseGet(),该方法按照字典的含义进行,该 orElseGet()部分仅在 Optional 为null时才会执行 。

Benchmarks:

基准

+--------------------+------+-----+------------+-------------+-------+
| Benchmark          | Mode | Cnt | Score      | Error       | Units |
+--------------------+------+-----+------------+-------------+-------+
| orElseBenchmark    | avgt | 20  | 60934.425  | ± 15115.599 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
| orElseGetBenchmark | avgt | 20  | 3.798      | ± 0.030     | ns/op |
+--------------------+------+-----+------------+-------------+-------+

Remarks: orElseGet()has clearly outperformed orElse()for our particular example.

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

Hope it clears the doubts of people like me who wants the very basic ground example :)

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

回答by Piyush N

First of all check the declaration of both the methods.

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

1) OrElse:Execute logic and pass result as argument.

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

public T orElse(T other) {    
 return value != null ? value : other;
}

2) OrElseGet:Execute logic if value inside the optional is null

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

public T orElseGet(Supplier<? extends T> other) {
  return value != null ? value : other.get(); 
}

Some explanation on above declaration:The argument of “Optional.orElse” always gets executed irrespective of the value of the object in optional (null, empty or with value). Always consider the above-mentioned point in mind while using “Optional.orElse”, otherwise use of “Optional.orElse” can be very risky in the following situation.

关于上述声明的一些解释:“Optional.orElse”的参数总是被执行,而不管可选对象的值(空、空或带值)。使用“Optional.orElse”时请始终牢记上述要点,否则在以下情况下使用“Optional.orElse”可能会非常危险。

Risk-1) Logging Issue:If content inside orElse contains any log statement: In this case, you will end up logging it every time.

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

Optional.of(getModel())
   .map(x -> {
      //some logic
   })
  .orElse(getDefaultAndLogError());

getDefaultAndLogError() {
  log.error("No Data found, Returning default");
  return defaultValue;
}

Risk-2) Performance Issue:If content inside orElse is time-intensive: Time intensive content can be any i/o operations DB call, API call, file reading. If we put such content in orElse(), the system will end up executing a code of no use.

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

Optional.of(getModel())
   .map(x -> //some logic)
   .orElse(getDefaultFromDb());

getDefaultFromDb() {
   return dataBaseServe.getDefaultValue(); //api call, db call.
}

Risk-3) Illegal State or Bug Issue:If content inside orElse is mutating some object state: We might be using the same object at another place let say inside Optional.map function and it can put us in a critical bug.

风险-3) 非法状态或错误问题:如果 orElse 中的内容正在改变某个对象状态:我们可能在另一个地方使用相同的对象,比如在 Optional.map 函数中,它可以把我们置于一个严重的错误中。

List<Model> list = new ArrayList<>();
Optional.of(getModel())
  .map(x -> {
  })
  .orElse(get(list));

get(List < String > list) {
   log.error("No Data found, Returning default");
   list.add(defaultValue);
   return defaultValue;
}

Then, When can we go with orElse()?Prefer using orElse when the default value is some constant object, enum. In all above cases we can go with Optional.orElseGet() (which only executes when Optional contains non empty value)instead of Optional.orElse(). Why?? In orElse, we pass default result value, but in orElseGet we pass Supplier and method of Supplier only executes if the value in Optional is null.

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

Key takeaways from this:

关键要点:

  1. Do not use “Optional.orElse” if it contains any log statement.
  2. Do not use “Optional.orElse” if it contains time-intensive logic.
  3. Do not use “Optional.orElse” if it is mutating some object state.
  4. Use “Optional.orElse” if we have to return a constant, enum.
  5. Prefer “Optional.orElseGet” in the situations mentioned in 1,2 and 3rd points.
  1. 如果“Optional.orElse”包含任何日志语句,请不要使用。
  2. 如果“Optional.orElse”包含时间密集型逻辑,请不要使用。
  3. 如果“Optional.orElse”正在改变某些对象状态,请不要使用它。
  4. 如果我们必须返回一个常量枚举,请使用“Optional.orElse”。
  5. 在第 1,2 和第 3 点中提到的情况下,更喜欢“Optional.orElseGet”。

I have explained this in point-2 (“Optional.map/Optional.orElse” != “if/else”) my medium blog. Use Java8 as a programmer not as a coder

我已经在第 2 点(“Optional.map/Optional.orElse”!=“if/else”)我的媒体博客中解释了这一点。使用 Java8 作为程序员而不是编码员