wake-up-neo.net

Unterschied zwischen `Optional.orElse ()` und `Optional.orElseGet ()`

Ich versuche den Unterschied zwischen den Methoden Optional<T>.orElse() und Optional<T>.orElseGet() zu verstehen. 

Die Beschreibung für die orElse()-Methode lautet "Gibt den Wert zurück, falls vorhanden, andernfalls einen anderen."

Die Beschreibung für die orElseGet()-Methode lautet zwar "Gibt den Wert zurück, falls vorhanden. Andernfalls rufen Sie other auf und geben das Ergebnis dieses Aufrufs zurück."

Die orElseGet()-Methode verwendet eine Supplier-Funktionsschnittstelle, die im Wesentlichen keine Parameter übernimmt und T zurückgibt. 

In welcher Situation müssten Sie orElseGet() verwenden? Wenn Sie eine Methode T myDefault() haben, warum würden Sie nicht optional.orElse(myDefault()) statt optional.orElseGet(() -> myDefault()) tun?

Es scheint nicht so, als würde orElseGet() die Ausführung des Lambda-Ausdrucks auf einen späteren Zeitpunkt oder etwas verschieben, was ist der Sinn davon? (Ich hätte gedacht, dass es sinnvoller wäre, wenn es einen sichereren Optional<T> zurückgibt, dessen get() niemals eine NoSuchElementException auslöst und isPresent() immer true zurückgibt ... aber offensichtlich nicht, es gibt nur T wie orElse() zurück).

Gibt es einen anderen Unterschied, den ich vermisse?

155
jbx

Nehmen Sie diese zwei Szenarien:

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

Wenn opt keinen Wert enthält, sind die beiden tatsächlich gleichwertig. Wenn optenthält einen Wert enthält, wie viele Foo-Objekte werden erstellt?

P.s .: In diesem Beispiel wäre der Unterschied wahrscheinlich nicht messbar, aber wenn Sie Ihren Standardwert beispielsweise von einem Remote-Webdienst oder von einer Datenbank erhalten müssen, wird dies plötzlich sehr wichtig.

133
biziclop

Ich griff hier nach dem Problem Kudo an.

Ich teile meine Erfahrung für andere.

orElse oder orElseGet, das ist die Frage:

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

druckt

B()...
A
A
50
Jin Kwon

Kurze Antwort: 

  • orElse () ruft die angegebene Funktion unabhängig vom Optional.isPresent()-Wert immer auf
  • orElseGet () ruft die angegebene Funktion nur auf, wenn Optional.isPresent() == false

In echtem Code sollten Sie den zweiten Ansatz in Betracht ziehen, wenn die benötigte Ressource teuer ist zu bekommen.

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

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

Betrachten Sie für weitere Details das folgende Beispiel mit dieser Funktion:

public Optional<String> findMyPhone(int phoneId)

Der Unterschied ist wie folgt: 

                           X : buyNewExpensivePhone() called

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

Bei optional.isPresent() == false gibt es keinen Unterschied zwischen zwei Möglichkeiten. Wenn jedoch optional.isPresent() == true aufgerufen wird, ruft orElse() die nachfolgende Funktion immer auf, ob Sie dies wünschen oder nicht. 

Schließlich wird der Testfall wie folgt verwendet:

Ergebnis: 

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

Ich würde sagen, der größte Unterschied zwischen orElse und orElseGet besteht, wenn wir etwas auswerten wollen, um den neuen Wert in der else-Bedingung zu erhalten.

Betrachten Sie dieses einfache Beispiel -

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

Lassen Sie uns nun das obige Beispiel in Optional zusammen mit orElse umwandeln.

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

Lassen Sie uns nun das obige Beispiel in Optional zusammen mit orElseGet umwandeln.

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

Wenn orElse aufgerufen wird, wird die apicall().value ausgewertet und an die Methode übergeben. Bei orElseGet erfolgt die Auswertung jedoch nur, wenn die oldValue leer ist. orElseGet ermöglicht eine faule Auswertung.

33
devang

Das folgende Beispiel soll den Unterschied verdeutlichen:

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

Die Antwort wird auch in den Dokumenten angezeigt.

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

Gibt den Wert zurück, falls vorhanden, andernfalls invoke other und gibt das Ergebnis dieses Aufrufs zurück.

Die Supplierwird nicht wird aufgerufen, wenn die Optional vorhanden ist. wohingegen,

public T orElse(T other) :

Geben Sie den Wert zurück, falls vorhanden, andernfalls geben Sie other zurück.

Wenn other eine Methode ist, die eine Zeichenfolge zurückgibt, wird sie aufgerufen, ihr Wert wird jedoch nicht zurückgegeben, falls die Optional vorhanden ist.

1
Maroun

Der Unterschied ist ziemlich subtil und wenn Sie nicht viel Aufmerksamkeit schenken, werden Sie es in einer falschen Weise verwenden.

Der beste Weg, um den Unterschied zwischen orElse() und orElseGet() zu verstehen, ist, dass orElse() immer ausgeführt wird, wenn Optional<T>null oder nicht, aber orElseGet() wird nur ausgeführt, wenn Optional<T>null ist.

Die Wörterbuchbedeutung von orElse ist : - führen Sie den Teil aus, wenn etwas nicht vorhanden ist, aber hier widerspricht es, siehe das folgende Beispiel:

    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 ist nicht NULL, dennoch werde ich ausgeführt


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

Ausgabe: emptyOptional ist NULL, ich werde ausgeführt, es ist normal wie im Wörterbuch

Für orElseGet() wird die Methode gemäß der Wörterbuchbedeutung ausgeführt. Der Teil orElseGet() wird nur ausgeführt, wenn Optional null ist.

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

Bemerkungen: orElseGet() hat orElse() für unser spezielles Beispiel deutlich übertroffen.

Hoffe, es klärt die Zweifel von Leuten wie mir, die das grundlegende Beispiel wollen:)

1
Vishwa Ratna

Betrachten Sie den folgenden 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();
  }
}

wenn wir value auf diese Weise erhalten: Optional.<String>ofNullable(null), besteht kein Unterschied zwischen orElseGet () und orElse (), aber wenn wir value auf diese Weise erhalten: Optional.<String>ofNullable("test") wird orelesMethod() in orElseGet() nicht aufgerufen, aber in orElse() aufgerufen

0
ratzip