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


206

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

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

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

このorElseGet()メソッドは、基本的にパラメーターをとらずにを返すサプライヤー機能インターフェースを取りますT

どの状況で使用する必要がありますorElseGet()か?あなたは、メソッドを持っている場合はT myDefault()、あなただけはしないだろう理由optional.orElse(myDefault())ではなくoptional.orElseGet(() -> myDefault())

orElseGet()ラムダ式の実行を後で延期しているようには見えないので、それの意味は何ですか?(私はそれがより安全に返された場合、それはより有用であろうと思っているだろうOptional<T>そのget()投げたことがないNoSuchElementExceptionisPresent()常にtrueを返します...しかし、明らかにそのない、それだけを返すTようにorElse())。

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


7
その理由は、orElseGet値を指定しない場合にのみサプライヤーを呼び出すときに使用するためです。
Alex Salauyou、2015年

9
わかった。したがってorElse()myDefault()メソッドの場合はまだ呼び出されますが、その戻り値は使用されません。
jbx

3
私は誤解を見てきたか、単に使用に忘れることは何からの理由Upvoted質問orElseGet():いくつかの重大なバグが発生することができますmedium.com/alphadev-thoughts/...
softarn

良い説明はここにあります:baeldung.com/java-optional-or-else-vs-or-else-get
Nestor Milyaev

回答:


172

次の2つのシナリオを考えます。

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

opt値が含まれていない場合、2つは実際に同等です。しかし、に値opt 含まれている場合、いくつのFooオブジェクトが作成されますか?

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


22
説明担当者に感謝します。そのため、違いはわずかですが重要です。2番目の場合は新しいFooオブジェクトを作成しませんが、1番目の場合は作成しますが、内に値がある場合は使用しませんOptional
jbx

5
@jbxはい、そして私のおかしな例では間違いなく実際の違いはありませんが、たとえばリモートWebサービスやデータベースからデフォルト値を取得する必要がある場合、その違いは突然非常に重要になります。
biziclop

2
@jbx:2つのことを混同しています。計算の結果を使用しないことによって単に引き起こされた奇妙なベンチマーク結果に関するSOに関する質問はすでにあります。JVM それを行うことができます。一方、System.out.println()は計算ではなく、観察可能な副作用を生じるステートメントです。そして、私はすでに観察可能な副作用が最適化を妨げると述べました(コンソール出力ストリーム外部リソースです)。
Holger

7
回答の代わりに質問が受け入れられたのはこれが初めてです。
キリルG.

4
" たとえば、リモートWebサービスからデフォルト値を取得する必要がある場合 "これはまさに私のシナリオでした。私の場合、オプションはクエリであり、クエリがない場合のデフォルトはすべての値をフェッチすることでした...そう、orElseGetはその操作の実行時間を1000倍短縮しました。
scottysseus

109

短い答え:

  • 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() == trueorElse()必要かどうかにかかわらず、常に後続の関数を呼び出します。

最後に、使用するテストケースは次のとおりです。

結果:

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

画像にエラーがあると思います。右側に「orElseGet」と表示されているはずです。その上、素晴らしい例です。
Yalla T.

はい。それで合っています。ありがとうございます:)次の数時間で更新します
nxhoaf 2018

2番目の箇条書きについては、Optional.isPresent() == false代わりに(false、trueではない)と思われる
Manuel Jordan

素晴らしい例-しかし、Optional.orElseどの州のJavadoc If a value is present, returns the value, otherwise returns otherがこの振る舞いを暗示することができるかについては、本当にわかりません...
Erik Finnman '23

あなたの説明に基づいて、私のためにそれはそのように見えるorElse()と似振る舞いfinallytry-catch表現。私は正しいですか?
マイクB.

63

私は工藤が言及した問題のためにここに行きました。

私は自分の経験を他の人と共有しています。

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()の値を評価します。したがって、orElseGet怠惰です。


7
それは「問題」ではありません。メソッドの引数がメソッドの実行前に評価されるのは簡単な事実です。B()という名前のメソッドに渡すorElse()か、abc()それが何の違いもない場合は、B()評価されます。
jbx 2017年

11
ここでの問題は、実際にはメソッドの名前です。orそれは我々がブール条件にするために使用されているものですので、それは、短絡操作であるとの考え方に(私は問題を尋ねられたとき私を含め)プレフィックス誤解の開発者。ただし、そうではありません。orプレフィックスに含まれているのは単なるメソッド名なのでOptional、値が含まれているかどうかに関係なく、引数は評価されます。名前の付け方がわかりにくいのは残念ですが、それについて何でもできるわけではありません。
jbx 2017年

37

条件の新しい値を取得するために何かを評価するときにorElse、との最大の違いがorElseGetわかりelseます。

この簡単な例を考えてみましょう-

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

では、上記の例をOptionalとともに使用するように変換してみましょうorElse

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

では、上記の例をOptionalとともに使用するように変換してみましょうorElseGet

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

ときにorElse呼び出され、apicall().value評価され、メソッドに渡されます。一方、orElseGet評価の場合はoldValueが空の場合にのみ発生します。orElseGet遅延評価を許可します。


4
ifElse()のこの「奇妙な」振る舞いのため、私は何度も無駄にしています。ifElse()よりもifElseGet()を優先する方が理にかなっていると思います
エンリコジュリン

3

次の例は、違いを示しています。

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)

存在する場合それ以外の場合、値を返したinvoke他のその呼び出しの結果を返します。

Supplier しません場合に呼び出されるOptionalプレゼント。一方、

public T orElse(T other)

存在する場合は値を返し、そうでない場合はその他を返します。

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


3

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

違いを理解するための最良の方法orElse()orElseGet()それがされorElse()た場合に常に実行されるOptional<T>ヌルない、しかし、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()パートが実行されるのは、オプションが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()ています。

それが私のような非常に基本的な地面の例を望んでいる人々の疑問をクリアしてくれることを願っています:)


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」の引数は、オプションのオブジェクトの値(null、空、または値付き)に関係なく常に実行されます。「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操作のDB呼び出し、API呼び出し、ファイルの読み取りです。そのようなコンテンツをorElse()に入れると、システムは無駄なコードを実行することになります。

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

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

リスク3)違法な状態またはバグの問題: orElse内のコンテンツが一部のオブジェクトの状態を変更している場合:別の場所で同じオブジェクトを使用している可能性があります。

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を使用することをお勧めします。上記のすべてのケースで、Optional.orElse()の代わりにOptional.orElseGet()(Optionalに空でない値が含まれている場合にのみ実行される)を使用できます。なぜ??orElseでは、デフォルトの結果値を渡しますが、orElseGetでは、Supplierを渡し、Optionalの値がnullの場合にのみ、Supplierのメソッドを実行します。

これからの重要なポイント:

  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」)で説明しました。コーダーとしてではなくプログラマーとしてJava8を使用する


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

valueこの方法で取得した場合:Optional.<String>ofNullable(null)、orElseGet()とorElse()の間に違いはありませんがvalue、この方法で取得した場合:Optional.<String>ofNullable("test")orelesMethod()in orElseGet()は呼び出されませんが、その中orElse()で呼び出されます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.