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

时间:2015-10-16 12:05:16

标签: java java-8 optional

我想了解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()永远不会抛出NoSuchElementExceptionisPresent()总是返回true ...但显然它不是,它只返回T之类的orElse()

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

8 个答案:

答案 0 :(得分:135)

采取以下两种情况:

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

如果opt没有包含值,则两者确实相同。但是,如果opt 包含值,那么将创建多少Foo个对象?

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

答案 1 :(得分:57)

简答:

  • orElse()将始终调用给定的函数,无论您是否需要它,无论Optional.isPresent()
  • orElseGet()只会在Optional.isPresent() == false
  • 时调用给定的函数

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

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

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

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

public Optional<String> findMyPhone(int phoneId)

区别如下:

                           X : buyNewExpensivePhone() called

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

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

代码:

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

答案 2 :(得分:54)

我在这里找到了Kudo提到的问题。

我与他人分享我的经验。

orElseorElseGet,这就是问题:

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

打印

B()...
A
A

答案 3 :(得分:34)

我想说orElseorElseGet之间的最大区别在于我们想要评估某些内容以获得else条件中的新值。

考虑这个简单的例子 -

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

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

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

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

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

调用orElse时,会计算apicall().value并将其传递给方法。然而,在orElseGet的情况下,仅在oldValue为空时才进行评估。 orElseGet允许延迟评估。

答案 4 :(得分:3)

区别非常细微,如果您不注意的话,则会以错误的方式使用它。

了解orElse()orElseGet()之间区别的最好方法是,如果orElse() null 或< strong>不是,但是Optional<T>仅在orElseGet()时执行。

orElse 的词典含义是:-在不存在某些内容但与之矛盾的情况下执行该部分,请参见以下示例:

Optional<T>
  

输出:nonEmptyOptional不为NULL,仍在执行中


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

输出:empty可选为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"; } ,该方法按照字典的含义进行操作,   orElseGet()部分仅在Optional为   

基准

orElseGet()
  

备注:对于我们的特定示例,+--------------------+------+-----+------------+-------------+-------+ | Benchmark | Mode | Cnt | Score | Error | Units | +--------------------+------+-----+------------+-------------+-------+ | orElseBenchmark | avgt | 20 | 60934.425 | ± 15115.599 | ns/op | +--------------------+------+-----+------------+-------------+-------+ | orElseGetBenchmark | avgt | 20 | 3.798 | ± 0.030 | ns/op | +--------------------+------+-----+------------+-------------+-------+ 明显胜过orElseGet()

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

答案 5 :(得分:2)

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

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

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

2)OrElseGet:如果可选字段中的值为null,则执行逻辑

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

有关上述声明的一些解释: 无论可选对象中的值是什么(空,空或带值),始终会执行“ Optional.orElse”的参数。使用“ Optional.orElse”时请始终牢记上述几点,否则在以下情况下使用“ Optional.orElse”可能会带来很大的风险。

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

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

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

风险2)性能问题::如果orElse中的内容耗时,请执行以下操作: 耗时的内容可以是任何I / O操作数据库调用,API调用,文件读取。如果将此类内容放入orElse(),系统将最终执行无用的代码。

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

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

风险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;
}

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

主要收获如下:

  1. 如果包含任何日志语句,请不要使用“ Optional.orElse”。
  2. 如果它包含耗时的逻辑,请不要使用“ Optional.orElse”。
  3. 如果要更改某些对象状态,请不要使用“ Optional.orElse”。
  4. 如果我们必须返回常量枚举,请使用“ Optional.orElse”。
  5. 在第1,2点和第3点提到的情况下,首选“ Optional.orElseGet”。

我已经在我的中等博客的第2点(“ Optional.map/Optional.orElse”!=“ if / else” )中对此进行了解释。 Use Java8 as a programmer not as a coder

答案 6 :(得分:1)

以下示例应说明差异:

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

答案也出现在文档中。

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

  

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

如果出现Supplier,则不会调用Optional 。而

public T orElse(T other)

  

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

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

答案 7 :(得分:0)

考虑以下代码:

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

如果我们以这种方式得到valueOptional.<String>ofNullable(null),则orElseGet()和orElse()之间没有区别,但是如果我们以这种方式得到value:{{1 }},Optional.<String>ofNullable("test")中的orelesMethod()将不会被调用,但在orElseGet()中它将被称为