web-dev-qa-db-ja.com

`Optional.orElse()`と `Optional.orElseGet()`の違い

Optional<T>.orElse() メソッドと Optional<T>.orElseGet() メソッドの違いを理解しようとしています。

orElse()メソッドの説明は、 "存在する場合は値を返し、それ以外の場合はotherを返します。"

一方、orElseGet()メソッドの説明はです。「存在する場合は値を返し、それ以外の場合はotherを呼び出してその呼び出しの結果を返します。」

orElseGet()メソッドはSupplier関数型インタフェースを取ります。これは基本的にパラメータを一切取りません。そしてTを返します。

どの状況でorElseGet()を使う必要がありますか?もしあなたがT myDefault()というメソッドを持っているなら、なぜあなたはoptional.orElse(myDefault())ではなくoptional.orElseGet(() -> myDefault())をやらないのですか?

orElseGet()がラムダ式の実行をしばらく後に延期しているようには見えませんが、そのポイントは何ですか? (get()が決してNoSuchElementExceptionをスローせず、isPresent()が常にtrueを返さないより安全なOptional<T>を返した方がより有用であると思いました。しかし明らかにそうではありません、orElse()のようにTを返すだけです).

私が見逃している他のいくつかの違いはありますか?

164
jbx

これら二つのシナリオを取りなさい:

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

optに値が含まれていない場合、この2つはまったく同じです。しかし、optに値が含まれている場合、Fooオブジェクトはいくつ作成されるのでしょうか。

Ps:もちろんこの例では違いは測定できないでしょうが、たとえばリモートWebサービスやデータベースからデフォルト値を取得する必要がある場合、それは突然非常に重要になります

137
biziclop

短い答え:

  • orElse()は、Optional.isPresent()の値に関係なく、必要に応じて指定された関数を常に呼び出します。
  • orElseGet()は、Optional.isPresent() == falseが指定された場合にのみ、指定された関数を呼び出します。

実際のコードでは、必要なリソースが取得にコストがかかる場合に、2番目のアプローチを検討することをお勧めします。

// 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の場合、2つの方法に違いはありません。ただし、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");
    }
}
59
nxhoaf

ここにたどり着きました 工藤 言及しました。

自分の経験を他の人にも伝えています。

それが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
54
Jin Kwon

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は遅延評価を可能にします。

34
devang

次の例で違いを説明します。

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)

存在する場合は値を返し、存在しない場合はotherを呼び出し、その呼び出しの結果を返します。

Supplierは、Optionalが存在する場合は呼び出されません。一方、

public T orElse(T other)

存在すれば値を返し、そうでなければotherを返します。

otherが文字列を返すメソッドの場合、それは呼び出されますが、Optionalが存在する場合はその値は返されません。

1
Maroun

違いはかなり微妙であり、あまり注意を払わないと、間違った方法で使用し続けることになります。

orElse()orElseGet()の違いを理解する最良の方法は、Optional<T>nullまたはnotの場合にorElse()が常に実行されることですが、orElseGet()は次の場合にのみ実行されますOptional<T>nullです。

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、実行されています、辞書によると正常です

orElseGet()の場合、メソッドはディクショナリの意味に従います。orElseGet()部分は、Optionalがnullの場合にのみ実行されます。

ベンチマーク

+--------------------+------+-----+------------+-------------+-------+
| 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
Vishwa Ratna

次のコードを考慮してください:

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")orelesMethod()orElseGet()は呼び出されませんが、orElse()では呼び出されます

0
ratzip