web-dev-qa-db-fra.com

Différence entre `Optional.orElse ()` et `Optional.orElseGet ()`

J'essaie de comprendre la différence entre les méthodes Optional<T>.orElse() et Optional<T>.orElseGet()

La description de la méthode orElse() est "Renvoie la valeur, le cas échéant, sinon renvoie autre."

Alors que, la description de la méthode orElseGet() est "Renvoie la valeur, le cas échéant, sinon invoquez autre et renvoie le résultat de cette invocation."

La méthode orElseGet() utilise une interface fonctionnelle fournisseur, qui ne prend généralement aucun paramètre et renvoie T

Dans quelle situation auriez-vous besoin d'utiliser orElseGet()? Si vous avez une méthode T myDefault(), pourquoi ne pas simplement faire optional.orElse(myDefault()) plutôt que optional.orElseGet(() -> myDefault())?

Il ne semble pas que orElseGet() reporte l'exécution de l'expression lambda à une date ultérieure, alors quel est le but de celle-ci? (J'aurais pensé qu'il serait plus utile de renvoyer un Optional<T> plus sûr dont get() ne jette jamais un NoSuchElementException et isPresent() toujours vrai ... mais évidemment non, il retourne simplement T comme orElse()).

Y a-t-il une autre différence qui me manque?

155
jbx

Prenons ces deux scénarios:

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

Si opt ne contient pas de valeur, les deux sont effectivement équivalents. Mais si optest-ce que contient une valeur, combien d'objets Foo seront créés?

P.s .: bien sûr, dans cet exemple, la différence ne serait probablement pas mesurable, mais si vous devez obtenir votre valeur par défaut d'un service Web distant, par exemple, ou d'une base de données, elle devient soudainement très importante.

133
biziclop

Je suis arrivé ici pour le problème Kudo mentionné.

Je partage mon expérience pour les autres.

orElse, ou orElseGet, telle est la question:

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

empreintes

B()...
A
A
50
Jin Kwon

Réponse courte: 

  • ouElse () appellera toujours la fonction donnée, que vous le vouliez ou non, indépendamment de la valeur de Optional.isPresent()
  • ouElseGet () n'appellera la fonction donnée que lorsque Optional.isPresent() == false

En code réel, vous pouvez envisager la seconde approche lorsque la ressource requise est coûteuse à obtenir .

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

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

Pour plus de détails, considérons l'exemple suivant avec cette fonction:

public Optional<String> findMyPhone(int phoneId)

La différence est comme ci-dessous: 

                           X : buyNewExpensivePhone() called

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

Lorsque optional.isPresent() == false, il n'y a pas de différence entre deux façons. Cependant, lorsque optional.isPresent() == true, orElse() appelle toujours la fonction suivante, que vous le vouliez ou non. 

Enfin, le cas de test utilisé est le suivant:

Résultat: 

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

Je dirais que la plus grande différence entre orElse et orElseGet survient lorsque nous voulons évaluer quelque chose pour obtenir la nouvelle valeur dans la condition else.

Considérez cet exemple simple -

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

Maintenant transformons l'exemple ci-dessus en utilisant Optional avec orElse,

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

Maintenant transformons l'exemple ci-dessus en utilisant Optional avec orElseGet,

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

Lorsque orElse est appelé, la fonction apicall().value est évaluée et transmise à la méthode. Alors que, dans le cas de orElseGet, l'évaluation n'a lieu que si oldValue est vide. orElseGet permet une évaluation paresseuse.

33
devang

L'exemple suivant devrait démontrer la différence:

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

La réponse apparaît également dans la documentation.

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

Renvoie la valeur si elle est présente, sinon invoke other et renvoie le résultat de cet appel.

La Supplierne sera pas être invoquée si la Optional présente. tandis que,

public T orElse(T other) :

Retourne la valeur si présente, sinon retourne autre.

Si other est une méthode qui renvoie une chaîne, elle sera invoquée, mais sa valeur ne sera pas renvoyée si la variable Optional existe.

1
Maroun

La différence est assez subtile et si vous ne payez pas beaucoup d'attention, vous continuerez à l'utiliser de manière erronée.

Le meilleur moyen de comprendre la différence entre orElse() et orElseGet() est que orElse() sera toujours exécuté si le Optional<T> est null ou not, Mais orElseGet() ne sera exécuté que lorsque Optional<T> sera null.

La signification du dictionnaire ouElse est : - exécute la partie quand quelque chose n'est pas présent, mais ici, cela contredit, voir l'exemple ci-dessous:

    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 n'est pas NULL, je suis toujours en cours d'exécution


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

Sortie: emptyOptional est NULL, je suis en cours d'exécution, c'est normal selon le dictionnaire

Pour orElseGet(), la méthode est conforme à la signification du dictionnaire. La partie orElseGet() ne sera exécutée que si Optional est null.

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

Remarques: orElseGet() a nettement surperformé orElse() pour notre exemple particulier.

J'espère que ça dissipe les doutes des gens comme moi qui veulent un exemple de base:)

1
Vishwa Ratna

Considérant le code suivant:

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

si nous obtenons value de cette manière: Optional.<String>ofNullable(null), il n'y a pas de différence entre orElseGet () et orElse (), mais si nous obtenons value de cette façon: Optional.<String>ofNullable("test"), orelesMethod() dans orElseGet() ne sera pas appelé mais dans orElse()

0
ratzip