`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()एक कभी नहीं फेंकता है NoSuchElementExceptionऔर isPresent()हमेशा सच रिटर्न ... लेकिन जाहिर है अपनी नहीं है, यह सिर्फ रिटर्न Tकी तरह orElse())।

क्या कुछ और अंतर है जो मुझे याद आ रहा है?


7
कारण यह है कि जब आप इसका उपयोग orElseGetकरते हैं तो आपूर्तिकर्ता को केवल तभी कॉल किया जाता है जब मूल्य अनुपस्थित हो।
एलेक्स सलाउउ

9
आह ठीक है समझ गया। में तो के मामले पद्धति अभी भी कहा जाता है, लेकिन इसकी वापसी मान उपयोग नहीं कर रहा है। orElse()myDefault()
jbx

3
उलझा हुआ प्रश्न क्योंकि मैंने जो गलतफहमी देखी है या केवल उपयोग करने की भूल orElseGet()कर रहा है, उसके परिणामस्वरूप कुछ गंभीर कीड़े हो सकते हैं: medium.com/alphadev-
हालांकिts/…

एक अच्छी व्याख्या यहाँ मिलती है: baeldung.com/java-optional-or-else-vs-or-else-get
Nestor Milyaev

जवाबों:


172

इन दो परिदृश्यों को लें:

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

यदि optकोई मान नहीं है, तो दोनों वास्तव में बराबर हैं। लेकिन अगर opt करता है एक मूल्य के होते हैं, कितने Fooवस्तुओं बनाया जाएगा?

Ps: बेशक इस उदाहरण में अंतर शायद औसत दर्जे का नहीं होगा, लेकिन यदि आपको अपना डिफ़ॉल्ट मान किसी दूरस्थ वेब सेवा से प्राप्त करना है, उदाहरण के लिए, या किसी डेटाबेस से, तो यह अचानक बहुत महत्वपूर्ण हो जाता है।


22
दोस्तो, स्पष्टीकरण के लिए धन्यवाद। इसलिए अंतर सूक्ष्म लेकिन महत्वपूर्ण है। दूसरे मामले में, यह एक नई Fooवस्तु नहीं बनाएगा , जबकि पहले मामले में इसे बनाएगा, लेकिन अगर इसका कोई मूल्य नहीं है, तो इसका उपयोग न करें Optional
jbx

5
@jbx हां, और मेरे अच्छे उदाहरण में यह यकीनन कोई वास्तविक अंतर नहीं रखता है, लेकिन अगर आपको किसी दूरस्थ वेब सेवा से अपना डिफ़ॉल्ट मान प्राप्त करना है, या डेटाबेस से, तो अंतर अचानक बहुत महत्वपूर्ण हो जाता है।
बिज़लोप

2
@jbx: आप दो चीजों को मिला रहे हैं। एसओ पर पहले से ही अजीब बेंचमार्क परिणामों के बारे में सवाल हैं जो केवल गणना के परिणाम का उपयोग नहीं करने के कारण थे। जेवीएम ऐसा कर सकता है। दूसरी ओर, System.out.println()है एक गणना लेकिन एक नमूदार पक्ष प्रभाव का उत्पादन एक बयान। और मैं पहले से ही कहा कि नमूदार दुष्प्रभाव अनुकूलन (कंसोल आउटपुट धारा में बाधा जाएगा है एक बाहरी संसाधन)।
होल्गर

7
यह पहली बार है जब मुझे एक उत्तर के बजाय एक प्रश्न दिखाई दे रहा है।
किरिल जी।

4
" यदि आपको उदाहरण के लिए किसी दूरस्थ वेब सेवा से अपना डिफ़ॉल्ट मान प्राप्त करना है " तो यह वास्तव में मेरा परिदृश्य था। मेरे मामले में, वैकल्पिक एक क्वेरी थी, और क्वेरी की अनुपस्थिति में डिफ़ॉल्ट सभी मूल्यों को प्राप्त करना था ... हाँ, orElseGet ने उस ऑपरेशन के रनटाइम को 1000 गुना कम कर दिया।
स्कूट्टीसियस

109

संक्षिप्त जवाब:

  • orElse () हमेशा दिए गए फ़ंक्शन को कॉल करेगा चाहे आप इसे चाहते हैं या नहीं, Optional.isPresent()मूल्य की परवाह किए बिना
  • orElseGet () केवल दिए गए फ़ंक्शन को कॉल करेगा जबOptional.isPresent() == false

वास्तविक कोड में, आप दूसरे दृष्टिकोण पर विचार करना चाह सकते हैं जब आवश्यक संसाधन प्राप्त करना महंगा होता है

// 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, दो तरीकों में कोई अंतर नहीं है। हालांकि, जब 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");
    }
}

मुझे लगता है कि आपके चित्र में त्रुटि हो सकती है, इसे दाईं ओर "orElseGet" कहना चाहिए? इसके अलावा, महान उदाहरण।
यल्ला टी।

हाँ आप सही हैं। धन्यवाद :) मैं इसे अगले कुछ घंटों में अपडेट
करूंगा

दूसरी गोली बिंदु के लिए, Optional.isPresent() == falseइसके बजाय (झूठा, सच नहीं) होना चाहिए
मैनुअल जॉर्डन

महान उदाहरण - लेकिन मुझे वास्तव में यह नहीं पता है कि Optional.orElseकिस राज्य के लिए जावदोक If a value is present, returns the value, otherwise returns otherइस व्यवहार का अर्थ लगा सकते हैं ...
एरिक फिनमैन

आपके स्पष्टीकरण के आधार पर, मेरे लिए ऐसा लगता है कि अभिव्यक्ति में orElse()समान व्यवहार होता है । क्या मैं सही हूँ? finallytry-catch
माइक बी।

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बी के मूल्य का मूल्यांकन करता है () वैकल्पिक के मूल्य पर निर्भर करता है। इस प्रकार, orElseGetआलसी है।


7
यह एक समस्या नहीं है'। यह सरल तथ्य है कि विधि के लिए तर्क का मूल्यांकन विधि निष्पादन से पहले किया जाता है। यदि आप B()नाम की विधि पास करते हैं orElse()या abc()इससे कोई अंतर नहीं पड़ता है, B()तो मूल्यांकन किया जाता है।
jbx

11
यहाँ मुद्दा वास्तव में विधियों के नामकरण का है। orउपसर्ग misleads डेवलपर्स (मेरे सहित जब मैं समस्या पूछा) सोच में है कि यह एक शॉर्ट-सर्किट आपरेशन है, क्योंकि यह है कि क्या हम बूलियन की स्थिति में किया जाता है। हालाँकि, यह नहीं है, यह सिर्फ एक विधि का नाम है जो orइसके उपसर्ग में है, इसलिए इसके तर्कों का मूल्यांकन किया जाएगा, भले ही Optionalकोई मूल्य ले रहा हो या नहीं। यह दुर्भाग्यपूर्ण है कि नामकरण भ्रामक है, न कि हम इसके बारे में कुछ भी कर सकते हैं।
jbx

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
इफ्लेस () के इस "अजीब" व्यवहार के कारण मैंने कई बार बर्बाद किया। मैं कहूंगा कि यह पसंद करता है कि ifElseet () से अधिक ifElse () पसंद करें
Enrico Giurin

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

यदि मौजूद है, तो मान लौटाएं, अन्यथा अन्य आह्वान करें और उस मंगलाचरण का परिणाम लौटाएं।

Supplier नहीं होगा यदि लागू किया जा Optionalप्रस्तुत। जहाँ तक,

public T orElse(T other):

यदि मौजूद है तो मान लौटाएं, अन्यथा अन्य वापस करें।

यदि otherएक विधि है जो एक स्ट्रिंग लौटाती है, तो इसे लागू किया जाएगा, लेकिन Optionalमौजूद होने की स्थिति में इसका मूल्य वापस नहीं होगा ।


3

अंतर बहुत सूक्ष्म है और अगर आप ज्यादा ध्यान नहीं देते हैं तो आप इसे गलत तरीके से इस्तेमाल करते रहेंगे।

सबसे अच्छा तरीका है के बीच अंतर को समझने के लिए orElse()और orElseGet()है कि है orElse()हमेशा अगर निष्पादित किया जाएगा Optional<T>है अशक्त या नहीं , लेकिन orElseGet()जब केवल निष्पादित किया जाएगा Optional<T>है अशक्त

Orlse का शब्दकोश अर्थ है : - उस भाग को निष्पादित करें जब कुछ मौजूद न हो, लेकिन यहां यह विरोधाभासी है, नीचे दिए गए उदाहरण देखें:

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

आउटपुट : रिक्त स्थान रिक्त है, मुझे निष्पादित किया जा रहा है, यह शब्दकोश के अनुसार सामान्य है

इसके लिए orElseGet(), विधि शब्दकोश अर्थ के अनुसार जाती है, इस orElseGet()भाग को केवल तभी निष्पादित किया जाएगा जब वैकल्पिक शून्य हो

बेंचमार्क :

+--------------------+------+-----+------------+-------------+-------+
| 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) आदेश: तर्क को निष्पादित करें और परिणाम को तर्क के रूप में पास करें।

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

2) OrElseGet: यदि वैकल्पिक के अंदर मान शून्य है तो तर्क को निष्पादित करें

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

उपरोक्त घोषणा पर कुछ स्पष्टीकरण: "Optional.orElse" का तर्क हमेशा वैकल्पिक (शून्य, रिक्त या मूल्य के साथ) में वस्तु के मूल्य के बावजूद निष्पादित होता है। हमेशा "Optional.orElse" का उपयोग करते समय उपर्युक्त बिंदु को ध्यान में रखें, अन्यथा निम्नलिखित स्थिति में "Optional.orElse" का उपयोग बहुत जोखिम भरा हो सकता है।

रिस्क -1) लॉगिंग इश्यू: अगर ऑर्ल के अंदर की सामग्री में कोई लॉग स्टेटमेंट है: तो इस स्थिति में, आप हर बार लॉगिंग को समाप्त कर देंगे।

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

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

जोखिम -2) प्रदर्शन का मुद्दा: अगर orlse के अंदर की सामग्री समय-गहन है: समय गहन सामग्री किसी भी i / o संचालन DB कॉल, एपीआई कॉल, फ़ाइल रीडिंग हो सकती है। यदि हम ऐसी सामग्री को orElse () में डालते हैं, तो सिस्टम बिना किसी उपयोग के कोड निष्पादित करेगा।

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

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

जोखिम -3) अवैध राज्य या बग का मुद्दा: यदि वस्तु के अंदर की सामग्री किसी वस्तु को बदल रही है: हम दूसरी जगह पर उसी वस्तु का उपयोग कर रहे होंगे जो Optional.map फ़ंक्शन के अंदर कहती है और यह हमें एक महत्वपूर्ण बग में डाल सकती है।

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 () के साथ कब जा सकते हैं? जब डिफ़ॉल्ट मान कुछ स्थिर ऑब्जेक्ट है, तो enum का उपयोग करना पसंद करें। उपरोक्त सभी मामलों में हम Optional.orElse () के बजाय Optional.orElseGet () के साथ जा सकते हैं (जो केवल तब निष्पादित होता है जब Optional में गैर रिक्त मान होता है)। क्यों?? OrElse में, हम डिफॉल्ट परिणाम मान पास करते हैं, लेकिन orElseGet में हम आपूर्तिकर्ता को पास करते हैं और आपूर्तिकर्ता की विधि केवल तभी निष्पादित होती है यदि वैकल्पिक में मान शून्य है।

इस से मुख्य takeaways:

  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()में orElseGet()नहीं कहा जा जाएगा, लेकिन में orElse()यह कहा जाएगा

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.