`Optional.orElse ()` ve `Optional.orElseGet ()` arasındaki fark


206

Ben arasındaki farkı anlamaya çalışıyorum Optional<T>.orElse()veOptional<T>.orElseGet() yöntemleri .

orElse()Yöntemin açıklaması "Varsa değeri döndür, aksi takdirde diğerini döndür" dür.

Bununla birlikte, orElseGet()yöntemin açıklaması "Varsa değeri döndür, aksi takdirde diğerini çağır ve bu çağrının sonucunu döndür."

orElseGet()Yöntem esasen herhangi bir parametre ve döner almaz bir alanı işlevsel bir arayüz alır T.

Hangi durumda kullanmanız gerekir orElseGet()? Eğer bir yönteminiz varsa, T myDefault()neden sadece yapmak optional.orElse(myDefault())istemesiniz ki optional.orElseGet(() -> myDefault())?

orElseGet()Lamda ifadesinin yürütülmesini daha sonraki bir zamana veya bir şeye erteliyor gibi görünmüyor, peki bunun anlamı nedir? (Ben daha güvenli bir iade durumunda daha yararlı olacağını sanırdım Optional<T>olan get()bir atar asla NoSuchElementExceptionve isPresent()her zaman doğru döndürür ... ama besbelli onun değil, sadece döner Tgibi orElse()).

Kaçırdığım başka bir fark var mı?


7
Bunun nedeni, orElseGetyalnızca değer yoksa tedarikçiyi çağırmasıdır.
Alex Salauyou

9
Ah tamam anladım. Bu nedenle orElse(), myDefault()yöntem hala çağrıldığında, ancak dönüş değeri kullanılmaz.
jbx

3
Yanlış anlama ya da kullanmayı unutmayı düşündüğümdenorElseGet() bazı ciddi hatalara neden olabilir: medium.com/alphadev-thoughts/…
softarn

Yanıtlar:


172

Şu iki senaryoyu ele alın:

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

Eğer optbir değer içermiyor, iki gerçekten eşdeğerdir. Ama eğer opt yok bir değeri içermesi, kaç Foonesneleri oluşturulur?

Ps: elbette bu örnekte fark muhtemelen ölçülemez, ancak varsayılan değerinizi uzak bir web hizmetinden veya bir veritabanından almak zorunda kalırsanız, aniden çok önemli hale gelir.


22
Açıklamalar için teşekkürler çocuklar. Bu yüzden fark ince ama anlamlıdır. İkinci durumda, yeni bir Foonesne oluşturmaz , ilk durumda onu oluşturur, ancak içinde bir değer varsa onu kullanmaz Optional.
jbx

5
@jbx Evet ve noddy örneğimde bu muhtemelen gerçek bir fark yaratmıyor, ancak varsayılan değerinizi uzak bir web hizmetinden veya bir veritabanından almanız gerekiyorsa, fark aniden çok önemli hale gelir.
biziclop

2
@jbx: iki şeyi karıştırıyorsunuz. SO üzerinde, bir hesaplama sonucunu kullanmamaktan kaynaklanan garip kıyaslama sonuçları hakkında zaten sorular var. JVM bunu yapabilir. Öte yandan, System.out.println()bir değil bir hesaplama ama gözlemlenebilir bir yan etkisi üreten bir ifade. Ve ben zaten gözlemlenebilir yan etkiler optimizasyonlar (konsol çıkış akımı engel olacağını söyledi olan bir dış kaynak).
Holger

7
İlk kez bir cevap kabul etmek yerine bir soru görüyorum.
Kirill G.

4
" Varsayılan değerinizi uzak bir web hizmetinden almanız gerekiyorsa " bu benim senaryomdu. Benim durumumda, isteğe bağlı bir sorgu vardı ve bir sorgu yokluğunda varsayılan tüm değerleri getirmekti ... evet, ya daElseGet bu işlemin çalışma süresini 1000 kat azalttı.
scottysseus

109

Kısa cevap:

  • orElse () , Optional.isPresent()değeri ne olursa olsun, ister istemesin ister istemesin her zaman verilen işlevi çağırır
  • orElseGet () yalnızca verilen işlevi yalnızcaOptional.isPresent() == false

Gerçek kodda, gerekli kaynağın elde edilmesi pahalı olduğunda ikinci yaklaşımı dikkate almak isteyebilirsiniz .

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

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

Daha fazla ayrıntı için, bu işlevle aşağıdaki örneği göz önünde bulundurun:

public Optional<String> findMyPhone(int phoneId)

Fark aşağıdaki gibidir:

                           X : buyNewExpensivePhone() called

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

Ne zaman optional.isPresent() == false, iki yol arasında fark yoktur. Bununla birlikte, ne zaman optional.isPresent() == true, orElse()isteseniz de istemeseniz de daima sonraki işlevi çağırır.

Son olarak, kullanılan test durumu aşağıdaki gibidir:

Sonuç:

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

Kod:

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

Resminizde bir Hata olabileceğini düşünüyorum, sağda "orElseGet" yazmalıdır? Bunun yanında harika bir örnek.
Yalla T.

Evet haklısın. Teşekkür ederim :) Birkaç saat içinde güncelleyeceğim
nxhoaf

İkinci mermi noktası Optional.isPresent() == falseyerine , görünüşe göre (yanlış, doğru değil)
Manuel Jordan

Harika bir örnek - ama Optional.orElsedevletlerin If a value is present, returns the value, otherwise returns otherbu davranışı ima edebileceği
Javadocların

Senin açıklama dayanarak, benim için öyle gibi gözüküyor orElse()benzer davranır finallyiçinde try-catchifade. Doğrumuyum?
Mike

63

Kudo'nun bahsettiği sorun için buraya geldim .

Deneyimlerimi başkaları için paylaşıyorum.

orElseya orElseGetda soru şu:

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

baskılar

B()...
A
A

orElseB () 'nin değerini isteğe bağlı olanın değerine bağlı olarak değerlendirir. Böylece orElseGettembeldir.


7
Sorun değil'. Bir yöntemin argümanının, yöntemin yürütülmesinden önce değerlendirilmesi basit bir gerçektir. B()Adlandırılmış bir yönteme geçerseniz orElse()veya abc()herhangi bir fark yaratmazsa, B()değerlendirilir.
jbx

11
Buradaki konu gerçekten yöntemlerin isimlendirilmesidir. orBiz boolean koşullarda kullanılır budur, çünkü kısa devre yapması olduğunu da düşünerek (Sorunu sorulduğunda ben dahil) önek misleads geliştiricileri. Ancak, sadece orönekinde olan bir yöntem adı değildir, bu nedenle argümanları Optionalbir değer taşıyıp taşımadığına bakılmaksızın değerlendirilecektir . İsminin kafa karıştırıcı olması talihsiz bir durumdur, bununla ilgili herhangi bir şey yapamayız.
jbx

37

Ben arasındaki en büyük farkı söyleyebilirim orElseve orElseGetbiz de yeni bir değer almak için bir şeyler değerlendirmek istediğinizde gelir elsedurumuna.

Bu basit örneği düşünün -

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

Şimdi kullanarak yukarıdaki örneği dönüşümü izin Optionalile birlikte orElse,

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

Şimdi kullanarak yukarıdaki örneği dönüşümü izin Optionalile birlikte orElseGet,

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

Zaman orElseçağrılır apicall().valuedeğerlendirilmiş ve yönteme geçirilir. Oysa orElseGetdeğerlendirme durumunda sadece oldValueboş olduğu zaman olur . orElseGettembel değerlendirmeye izin verir.


4
İfElse () 'nin bu "garip" davranışı nedeniyle birçok kez boşa harcadım. IfElse () yerine ifElseGet () tercih etmenin mantıklı olduğunu söyleyebilirim
Enrico Giurin

3

Aşağıdaki örnek farkı göstermelidir:

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

Cevap dokümanlarda da görünür.

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

Varsa değeri döndürün , aksi takdirde diğerini çağırın ve bu çağrının sonucunu döndürün.

Supplier Olmaz ise çağrılabilir Optionalhediyeler. buna karşılık,

public T orElse(T other):

Varsa değeri döndürün, aksi takdirde diğerini döndürün.

Eğer otherbir dize döndüren bir yöntemdir, bu çağrılır, ama onun değeri durumunda iade edilmeyecektir Optionalvar.


3

Fark oldukça incedir ve fazla dikkat etmezseniz, yanlış bir şekilde kullanmaya devam edersiniz.

En iyi yolu arasındaki farkı anlamak orElse()ve orElseGet()yani orElse()eğer hep yürütülecektir Optional<T>olan boş veya değil , ama orElseGet()yalnızca çalıştırılacaktır Optional<T>olan boş .

OrElse'un sözlük anlamı şudur : - bir şey olmadığında parçayı yürütün, ancak burada çelişir, aşağıdaki örneğe bakın:

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

Çıktı: nonEmptyOptional NULL değil, yine de yürütülüyorum


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

Çıktı : emptyOptional NULL, yürütülüyor, sözlüğe göre normal

İçin orElseGet(), Yöntem sözlük anlamına göre gider, orElseGet()Parça yalnızca İsteğe Bağlı boş olduğunda yürütülür .

Deneyler :

+--------------------+------+-----+------------+-------------+-------+
| Benchmark          | Mode | Cnt | Score      | Error       | Units |
+--------------------+------+-----+------------+-------------+-------+
| orElseBenchmark    | avgt | 20  | 60934.425  | ± 15115.599 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
| orElseGetBenchmark | avgt | 20  | 3.798      | ± 0.030     | ns/op |
+--------------------+------+-----+------------+-------------+-------+

Açıklamalar : özel örneğimiz için orElseGet()açıkça daha iyi performans gösterdi orElse().

Umarım benim gibi temel zemin örneğini isteyen insanların şüphelerini temizler :)


2

Her şeyden önce her iki yöntemin beyanını kontrol edin.

1) OrElse: Mantık yürütün ve sonucu bağımsız değişken olarak iletin.

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

2) OrElseGet: İsteğe bağlı içindeki değer null ise mantığı yürütün

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

Yukarıdaki bildirimle ilgili bazı açıklamalar: “İsteğe bağlı.veyaElse” bağımsız değişkeni, nesnenin isteğe bağlı (null, boş veya değer) değerinden bağımsız olarak her zaman yürütülür. “Optional.orElse” kullanırken daima yukarıda belirtilen noktayı göz önünde bulundurun, aksi takdirde “Optional.orElse” kullanımı aşağıdaki durumda çok riskli olabilir.

Risk-1) Günlüğe Kaydetme Sorunu: orElse içindeki içerik herhangi bir günlük ifadesi içeriyorsa: Bu durumda, bunu her seferinde günlüğe kaydedersiniz.

Optional.of(getModel())
   .map(x -> {
      //some logic
   })
  .orElse(getDefaultAndLogError());

getDefaultAndLogError() {
  log.error("No Data found, Returning default");
  return defaultValue;
}

Risk-2) Performans Sorunu: orElse içindeki içerik zaman yoğun ise: Zaman yoğun içerik herhangi bir G / Ç işlemi DB çağrısı, API çağrısı, dosya okuma olabilir. Bu tür içeriği orElse () içine koyarsak, sistem sonuç olarak kullanılmayan bir kod yürütür.

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

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

Risk-3) Yasadışı Durum veya Hata Sorunu: orElse içindeki içerik bir nesne durumunu mutasyona uğratıyorsa: Aynı nesneyi başka bir yerde kullanıyor olabiliriz.

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

O zaman, orElse () ile ne zaman gidebiliriz? Varsayılan değer sabit bir nesne, enum olduğunda orElse kullanmayı tercih edin. Yukarıdaki tüm durumlarda Optional.orElse () yerine Optional.orElseGet () (yalnızca İsteğe bağlı boş olmayan değer içeriyorsa yürütülür) ile gidebiliriz. Neden?? OrElse'de varsayılan sonuç değerini geçiririz, ancak orElseGet'te Tedarikçi'yi ve Tedarikçi yöntemini yalnızca İsteğe Bağlı'daki değer null olduğunda yürütür.

Buradan önemli çıkarımlar:

  1. Herhangi bir günlük ifadesi içeriyorsa “Optional.orElse” kullanmayın.
  2. Yoğun zaman gerektiren bir mantık içeriyorsa “Optional.orElse” kullanmayın.
  3. Bazı nesne durumlarını değiştiriyorsa “Optional.orElse” kullanmayın.
  4. Sabit bir enum döndürmek zorunda kalırsak “Optional.orElse” kullanın.
  5. 1,2 ve 3. noktalarda belirtilen durumlarda “Optional.orElseGet” seçeneğini tercih edin.

Bunu ikinci blogumda ( “Optional.map/Optional.orElse”! = “İf / else” ) açıkladım . Java8'i kodlayıcı olarak değil programcı olarak kullanma


0

Aşağıdaki kod dikkate alındığında:

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

Biz alırsanız valuebu şekilde: Optional.<String>ofNullable(null), orElseGet () ve OrElse () arasında fark yoktur, ama biz alırsanız valuebu şekilde: Optional.<String>ofNullable("test"), orelesMethod()içinde orElseGet()adlandırılan olmayacak ama orElse()bunun adı verilecek

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.