' option . orelse() '和' option . orelseget() '的区别

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

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

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

orElseGet()方法接受一个Supplier函数接口,该接口本质上不接受任何参数并返回T

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

orElseGet()似乎并没有将lambda表达式的执行推迟到以后的某个时间之类的,那么它的意义是什么呢?(我认为如果它返回一个更安全的Optional<T>,其get()永远不会抛出NoSuchElementException,而isPresent()总是返回true,那么它会更有用……但显然不是,它只是像orElse()一样返回T)。

还有其他的不同吗?

186073 次浏览

以以下两种情况为例:

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

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

附注:当然,在这个例子中,差异可能无法测量,但如果您必须从远程web服务或数据库获取默认值,那么它就变得非常重要了。

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

我在和别人分享我的经验。

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

打印

B()...
A
A

orElse计算B()的值与optional的值相互依赖。因此,orElseGet是懒惰的。

我想说,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允许惰性求值。

简短的回答:

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

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

// 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 (Redundant call)
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 (Redundant call)");
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");
}
}

考虑以下代码:

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

如果我们以这种方式获得value: Optional.<String>ofNullable(null),那么orElseGet()和orElse()之间没有区别,但如果我们以这种方式获得value: Optional.<String>ofNullable("test")orElseGet()中的orelesMethod()将不会被调用,但在orElse()中它将被调用

下面的例子将说明两者的区别:

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


Optional<String> opt = Optional.of("Save the world");


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


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

答案也会出现在文档中。

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

如果存在则返回值,否则调用 other并返回

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

public T orElse(T other):

如果存在则返回值,否则返回other。

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

区别是相当微妙的,如果你不太注意,那么你就会以错误的方式使用它。

理解orElse()orElseGet()之间区别的最好方法是,如果Optional<T>orElse()将始终被执行,但orElseGet()只会在Optional<T>时被执行。

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

输出: 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";
}

输出: emptyOptional是NULL,我正在执行,这是正常的每 字典< / p >

对于orElseGet(),该方法按照字典的含义执行 orElseGet()部分将仅在Optional为时执行 空> < /强。

基准:

+--------------------+------+-----+------------+-------------+-------+
| 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()明显优于orElse()

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

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

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

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

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

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

以上声明说明: 可选的参数。orElse "总是被执行,而不管可选对象的值(null, empty或with value)。在使用“可选”时,一定要考虑到上述几点。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操作DB调用,API调用,文件读取。如果我们把这样的内容放在orElse()中,系统最终将执行一个无用的代码

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


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

风险-3)非法状态或Bug发布:如果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()? 当默认值是某个常量对象时,首选使用orElse。在上述所有情况下,我们都可以使用option . orelseget()(仅在Optional包含空值时执行)而不是option . orelse()。为什么? ?在orElse中,我们传递默认的结果值,但在orElseGet中,我们传递Supplier和Supplier的方法,只有当Optional中的值为null时才执行

这里的关键要点是:

  1. 不要使用“可选”。orElse”,如果它包含任何日志语句。
  2. 不要使用“可选”。orElse”,如果它包含时间密集型逻辑。
  3. 不要使用“可选”。如果它正在改变某个对象状态,则使用orElse。
  4. 使用“可选的。orElse”如果我们必须返回一个常量,enum。
  5. 更喜欢“可选的。orElseGet”在1、2和3点提到的情况下。

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

它们都用于返回一个可选的默认值,但如果使用一个方法来产生默认值:

  • orElse:总是执行该方法,如果Optional不为空也是如此
  • orElseGet:仅当Optional为空时才执行(+性能!)

请看这个例子(类OptionalExample):

public static void main(String[] args) {


Optional<String> optionalNotEmpty = Optional.of("StringVal");
    

// orElse: getDefaultValue called (useless)
System.out.println(optionalNotEmpty.orElse(getDefaultValue()));
    

// orElseGet: getDefaultValue not called (better solution)
System.out.println(optionalNotEmpty.orElseGet(OptionalExample::getDefaultValue));
}


public static String getDefaultValue() {
System.out.println("called");
return "default value";
}

输出:

called
StringVal
StringVal