RxJava में आप फ्लैट बनाम मैप मैप का उपयोग कब करते हैं?


180

आप mapबनाम RxJavaflatMap में कब प्रयोग करते हैं ?

उदाहरण के लिए, हम JSON से युक्त स्ट्रिंग्स में JSON युक्त फ़ाइलों को मैप करना चाहते हैं -

का उपयोग करते हुए map, हम Exceptionकिसी भी तरह से निपटने के लिए है । पर कैसे?:

Observable.from(jsonFile).map(new Func1<File, String>() {
    @Override public String call(File file) {
        try {
            return new Gson().toJson(new FileReader(file), Object.class);
        } catch (FileNotFoundException e) {
            // So Exception. What to do ?
        }
        return null; // Not good :(
    }
});

का उपयोग करना flatMap, यह बहुत अधिक क्रिया है, लेकिन हम समस्या की श्रृंखला को आगे बढ़ा सकते हैं Observablesऔर त्रुटि को संभाल सकते हैं यदि हम कहीं और चुनते हैं और यहां तक ​​कि पुन: प्रयास करते हैं:

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(final File file) {
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override public void call(Subscriber<? super String> subscriber) {
                try {
                    String json = new Gson().toJson(new FileReader(file), Object.class);

                    subscriber.onNext(json);
                    subscriber.onCompleted();
                } catch (FileNotFoundException e) {
                    subscriber.onError(e);
                }
            }
        });
    }
});

मैं की सादगी पसंद है map, लेकिन त्रुटि की हैंडलिंग flatmap(क्रिया नहीं)। मैंने इस तैरते हुए किसी भी सर्वोत्तम अभ्यास को नहीं देखा है और मैं उत्सुक हूं कि यह कैसे अभ्यास में उपयोग किया जा रहा है।

जवाबों:


121

mapएक घटना को दूसरे में बदलना। flatMapएक घटना को शून्य या अधिक घटना में बदलना। (यह IntroToRx से लिया गया है )

जैसा कि आप अपने जोंस को ऑब्जेक्ट में बदलना चाहते हैं, मैप का उपयोग पर्याप्त होना चाहिए।

FileNotFoundException से निपटना एक अन्य समस्या है (मानचित्र या फ़्लैटमैप का उपयोग करना इस समस्या को हल नहीं करेगा)।

अपनी अपवाद समस्या को हल करने के लिए, इसे एक गैर-चेक किए गए अपवाद के साथ फेंक दें: RX आपके लिए ऑनरर हैंडलर कहेगा।

Observable.from(jsonFile).map(new Func1<File, String>() {
    @Override public String call(File file) {
        try {
            return new Gson().toJson(new FileReader(file), Object.class);
        } catch (FileNotFoundException e) {
            // this exception is a part of rx-java
            throw OnErrorThrowable.addValueAsLastCause(e, file);
        }
    }
});

फ्लैटमैप के साथ सटीक समान संस्करण:

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(File file) {
        try {
            return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
        } catch (FileNotFoundException e) {
            // this static method is a part of rx-java. It will return an exception which is associated to the value.
            throw OnErrorThrowable.addValueAsLastCause(e, file);
            // alternatively, you can return Obersable.empty(); instead of throwing exception
        }
    }
});

आप एक नए ऑब्जर्वेबल के फ्लैटपेज संस्करण में भी लौट सकते हैं, जो कि केवल एक त्रुटि है।

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(File file) {
        try {
            return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
        } catch (FileNotFoundException e) {
            return Observable.error(OnErrorThrowable.addValueAsLastCause(e, file));
        }
    }
});

2
यह कॉल नहीं करता है subscriber.onError()आदि मैंने जितने भी उदाहरण देखे हैं उनमें सभी तरह की त्रुटियां हैं। क्या इससे कोई फर्क नहीं पड़ता?
क्रिस्टोफर पेरी

7
ध्यान दें कि के निर्माता OnErrorThrowableहैं privateऔर आपको OnErrorThrowable.from(e)इसके बजाय उपयोग करने की आवश्यकता है ।
david.mihola

मैंने अभी-अभी अपडेट किया है। OnErrorThrowable.from (e) मान नहीं रखते हैं, इसलिए मैं इसके बजाय OnErrorThrowable.addValueAsLastCause (e, file) का उपयोग करता हूं, जिससे मूल्य रखना चाहिए।
dwursteisen

1
मुझे कोड के उदाहरण पसंद हैं, लेकिन यदि आपने फ्लैट स्ट्रिंग कॉल के हस्ताक्षर को अपडेट किया है, तो केवल स्ट्रिंग के बजाय ऑब्जर्वेबल <स्ट्रिंग> वापस करने के लिए ... क्योंकि तकनीकी रूप से दोनों के बीच अंतर नहीं है?
रिच एहमर

78

फ्लैटपाइप बहुत अधिक नक्शे जैसा व्यवहार करता है, अंतर यह है कि यह जिस फ़ंक्शन को लागू करता है वह एक अवलोकन योग्य रिटर्न देता है, इसलिए यह अतुल्यकालिक संचालन पर मैप करने के लिए पूरी तरह से अनुकूल है।

व्यावहारिक अर्थों में, फ़ंक्शन मैप लागू होता है बस जंजीर प्रतिक्रिया पर एक परिवर्तन करता है (एक अवलोकन वापस नहीं); जब फ़ंक्शन फ़्लैटमैप लागू होता है Observable<T>, तो यही कारण है कि यदि आप विधि के अंदर एक अतुल्यकालिक कॉल करने की योजना बनाते हैं, तो फ़्लैटमैप की सिफारिश की जाती है।

सारांश:

  • मानचित्र प्रकार T की एक वस्तु देता है
  • फ़्लैटबॉल एक अवलोकन योग्य रिटर्न देता है।

एक स्पष्ट उदाहरण यहां देखा जा सकता है: http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk

काउचबेस जावा 2.X क्लाइंट एक सुविधाजनक तरीके से अतुल्यकालिक कॉल प्रदान करने के लिए आरएक्स का उपयोग करता है। चूँकि यह Rx का उपयोग करता है, इसमें मेथड्स मैप और फ़्लैटमैप हैं, उनके प्रलेखन में स्पष्टीकरण सामान्य अवधारणा को समझने में मददगार हो सकता है।

त्रुटियों को संभालने के लिए, अपने susbcriber पर ऑनराइड को ओवरराइड करें।

Subscriber<String> mySubscriber = new Subscriber<String>() {
    @Override
    public void onNext(String s) { System.out.println(s); }

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }
};

इस दस्तावेज़ को देखने में मदद मिल सकती है: http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

आरएक्स के साथ त्रुटियों का प्रबंधन करने के बारे में एक अच्छा स्रोत यहां पाया जा सकता है: https://gist.github.com/daschl/db9fcc9d2b932115b679


सारांश गलत है। मैप और फ्लैटपाइप एक ही प्रकार से लौटते हैं, लेकिन वे जिस फ़ंक्शन को लागू करते हैं वह अलग प्रकार का होता है।
CoXier

61

आपके मामले में आपको मानचित्र की आवश्यकता है, क्योंकि केवल 1 इनपुट और 1 आउटपुट है।

मानचित्र - प्रदत्त फ़ंक्शन केवल एक आइटम को स्वीकार करता है और एक आइटम देता है जिसे आगे (केवल एक बार) नीचे उत्सर्जित किया जाएगा।

फ्लैटपाइप - सप्लाई किया गया फंक्शन एक आइटम को स्वीकार करता है, फिर एक "ऑब्जर्वेबल" देता है, जिसका अर्थ है कि नए "ऑब्जर्वेबल" के प्रत्येक आइटम को अलग से नीचे उत्सर्जित किया जाएगा।

हो सकता है कोड आपके लिए चीजों को साफ कर दे:

Observable.just("item1").map( str -> {
    System.out.println("inside the map " + str);
    return str;
}).subscribe(System.out::println);

Observable.just("item2").flatMap( str -> {
    System.out.println("inside the flatMap " + str);
    return Observable.just(str + "+", str + "++" , str + "+++");
}).subscribe(System.out::println);

आउटपुट:

inside the map item1
item1
inside the flatMap item2
item2+
item2++
item2+++

यकीन नहीं है कि एक मानचित्र का उपयोग करना सबसे अच्छा विचार है, हालांकि यह काम करेगा। मान लीजिए कि FileReader को एसिंक्रोनस कॉल बनना था। फिर आपको मैप को एक फ्लैटपाइप में बदलना होगा। इसे एक नक्शे के रूप में छोड़ने का मतलब होगा कि आप घटनाओं को उम्मीद के मुताबिक निकाल नहीं पाएंगे और इससे भ्रम पैदा होगा। मैं इसे कुछ बार काट चुका हूं क्योंकि मैं अभी भी आरएक्स जावा सीख रहा हूं। मुझे लगता है कि फ्लैटपाइप आप की उम्मीद के अनुसार चीजों को सुनिश्चित करने का सुनिश्चित तरीका है।
user924272

24

जिस तरह से मैं इसके बारे में सोचता हूं वह यह है कि आप flatMapउस फ़ंक्शन का उपयोग करते हैं जो आप map()रिटर्न के अंदर डालना चाहते थे Observable। जिस स्थिति में आप अभी भी उपयोग करने की कोशिश कर सकते हैं map()लेकिन यह अव्यावहारिक होगा। मुझे समझाने की कोशिश क्यों करते हैं।

अगर ऐसे मामले में आपने साथ रहना तय किया है map, तो आपको ए Observable<Observable<Something>>। आपके मामले में उदाहरण के लिए, यदि हमने एक काल्पनिक RxGson लाइब्रेरी का उपयोग किया है, जो कि Observable<String>इसे toJson()विधि से लौटाया है (इसके बजाय केवल a String) यह इस तरह दिखेगा:

Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
    @Override public Observable<String>> call(File file) {
        return new RxGson().toJson(new FileReader(file), Object.class);
    }
}); // you get Observable<Observable<String>> here

इस बिंदु पर इस subscribe()तरह के अवलोकन के लिए यह बहुत मुश्किल होगा । इसके अंदर आपको एक ऐसा मिलेगा Observable<String>जिसके लिए आपको फिर subscribe()से मूल्य प्राप्त करने की आवश्यकता होगी । जो देखने में व्यावहारिक या अच्छा न हो।

तो इसे उपयोगी बनाने के लिए एक विचार "समतल" करना है, जो कि वेधशालाओं का अवलोकन है (आप यह देखना शुरू कर सकते हैं कि _flat_Map नाम कहां से आता है)। RxJava वेधशालाओं को समतल करने के लिए कुछ तरीके प्रदान करता है और सादगी के लिए यह मर्ज करना चाहता है कि हम क्या चाहते हैं। मर्ज मूल रूप से वेधशालाओं का एक गुच्छा लेता है और जब भी उनमें से कोई भी उत्सर्जन करता है तो उत्सर्जन करता है। (बहुत से लोगों का तर्क होगा कि स्विच एक बेहतर डिफ़ॉल्ट होगा। लेकिन यदि आप सिर्फ एक मूल्य का उत्सर्जन कर रहे हैं, तो यह वैसे भी मायने नहीं रखता है।)

तो हमारे पिछले स्निपेट में संशोधन करके हमें यह मिलेगा:

Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
    @Override public Observable<String>> call(File file) {
        return new RxGson().toJson(new FileReader(file), Object.class);
    }
}).merge(); // you get Observable<String> here

यह बहुत अधिक उपयोगी है, क्योंकि इसकी सदस्यता (या मैपिंग, या फ़िल्टरिंग, या ...) आपको बस Stringमूल्य मिलता है । (साथ ही, आप का ध्यान रखें, merge()RxJava में ऐसा कोई वैरिएंट मौजूद नहीं है, लेकिन यदि आप मर्ज के विचार को समझते हैं तो मुझे उम्मीद है कि आप भी समझ जाएंगे कि यह कैसे काम करेगा।)

इसलिए मूल रूप से क्योंकि ऐसा merge()शायद केवल तभी उपयोगी होना चाहिए जब यह map()एक अवलोकनीय वापसी को सफल करता है और इसलिए आपको इसे बार-बार टाइप करने की आवश्यकता नहीं है, flatMap()इसे शॉर्टहैंड के रूप में बनाया गया था। यह एक सामान्य map()इच्छा के रूप में मानचित्रण फ़ंक्शन को लागू करता है , लेकिन बाद में लौटाए गए मूल्यों का उत्सर्जन करने के बजाय यह उन्हें "समतल" (या विलय) करता है।

यह सामान्य उपयोग का मामला है। यह एक कोडबेस में सबसे अधिक उपयोगी है जो आरएक्स एलोवर का उपयोग करता है और आपको कई तरीके मिलते हैं जो कि वेधशालाओं को लौटाते हैं, जिसे आप अन्य तरीकों से देखना चाहते हैं।

आपके उपयोग के मामले में यह उपयोगी होने के साथ-साथ होता है, क्योंकि map()केवल उत्सर्जित एक मूल्य onNext()को दूसरे उत्सर्जित में बदल सकता है onNext()। लेकिन यह इसे कई मूल्यों में नहीं बदल सकता है, कोई मूल्य नहीं है या एक त्रुटि है। और जैसा कि akarnokd ने अपने उत्तर में लिखा है (और मन में है कि वह मुझसे ज्यादा चालाक है, शायद सामान्य तौर पर, लेकिन कम से कम जब यह RxJava की बात आती है) तो आपको अपने अपवादों को नहीं फेंकना चाहिए map()। तो इसके बजाय आप उपयोग कर सकते हैं flatMap()और

return Observable.just(value);

जब सब ठीक हो जाता है, लेकिन

return Observable.error(exception);

जब कुछ विफल होता है।
एक संपूर्ण स्निपेट के लिए उसका उत्तर देखें: https://stackoverflow.com/a/30330772/1402641


1
यह मेरा पसंदीदा जवाब है। आप मूल रूप से एक अवलोकनीय का अवलोकन करते हैं, यदि आपकी विधि वापस आती है।
गंदी_विलास

21

सवाल यह है कि RxJava में आप फ्लैट बनाम मैप मैप का उपयोग कब करते हैं? । और मुझे लगता है कि एक साधारण डेमो अधिक विशिष्ट है।

जब आप किसी अन्य प्रकार से उत्सर्जित आइटम को परिवर्तित करना चाहते हैं, तो आपके मामले में फ़ाइल को स्ट्रिंग, मानचित्र और फ़्लैटफ़ॉर्म्स में परिवर्तित करने से दोनों काम हो सकते हैं। लेकिन मैं मानचित्र ऑपरेटर को पसंद करता हूं क्योंकि यह अधिक स्पष्ट रूप से है।

हालाँकि किसी जगह पर flatMapजादू का काम mapकर सकते हैं लेकिन नहीं कर सकते। उदाहरण के लिए, मैं एक उपयोगकर्ता की जानकारी प्राप्त करना चाहता हूं, लेकिन उपयोगकर्ता लॉगिन करते समय मुझे सबसे पहले उसकी आईडी प्राप्त करनी होगी। जाहिर है मुझे दो अनुरोधों की आवश्यकता है और वे क्रम में हैं।

शुरू करते हैं।

Observable<LoginResponse> login(String email, String password);

Observable<UserInfo> fetchUserInfo(String userId);

यहाँ दो तरीके हैं, एक लॉगिन लौटाया गया Responseऔर दूसरा उपयोगकर्ता जानकारी प्राप्त करने के लिए।

login(email, password)
        .flatMap(response ->
                fetchUserInfo(response.id))
        .subscribe(userInfo -> {
            // get user info and you update ui now
        });

जैसा कि आप देख रहे हैं, फ़ंक्शन फ़्लैटफ़ॉर्म लागू होता है, सबसे पहले मैं उपयोगकर्ता आईडी प्राप्त करता हूं Responseफिर उपयोगकर्ता जानकारी प्राप्त करता हूं । जब दो अनुरोध समाप्त हो जाते हैं, तो हम अपना काम कर सकते हैं जैसे कि यूआई को अपडेट करना या डेटाबेस में डेटा को सहेजना।

हालाँकि यदि आप उपयोग mapकरते हैं तो आप इस तरह का अच्छा कोड नहीं लिख सकते हैं। एक शब्द में, flatMapअनुरोधों को क्रमबद्ध करने में हमारी सहायता कर सकता है।


18

यहाँ एक सरल है अंगूठे के नियम है कि मैं मदद मुझे जब उपयोग करने के लिए के रूप में तय का उपयोग flatMap()अधिक map()आरएक्स के दशक में Observable

एक बार जब आप एक निर्णय लेते हैं कि आप एक mapपरिवर्तन को नियोजित करने जा रहे हैं , तो आप किसी वस्तु को सही वापस करने के लिए अपना परिवर्तन कोड लिखेंगे?

यदि आप अपने परिवर्तन के अंतिम परिणाम के रूप में लौट रहे हैं:

  • एक अवलोकनीय वस्तु तो आप अभी उपयोग करेंगेmap() । और map()उस वस्तु को एक वेधशाला में लपेटता है और उसका उत्सर्जन करता है।

  • एक Observableवस्तु, तो आप उपयोग करेंगेflatMap() । और flatMap()ऑब्जर्वेबल को अलिखित करता है, लौटी हुई वस्तु को उठाता है, उसे अपने ऑब्जर्वेबल से लपेटता है और उसका उत्सर्जन करता है।

उदाहरण के लिए कहें कि हमने एक विधि टाइटलकैस (स्ट्रिंग इनपुटपराम) है जो इनपुट परम के शीर्षक वाले शीर्षक वाले कैसिंग स्ट्रिंग ऑब्जेक्ट को लौटाता है। इस पद्धति का वापसी प्रकार Stringया हो सकता है Observable<String>

  • यदि रिटर्न प्रकार titleCase(..)केवल होना था String, तो आप उपयोग करेंगेmap(s -> titleCase(s))

  • यदि वापसी का प्रकार titleCase(..)होना था Observable<String>, तो आप उपयोग करेंगेflatMap(s -> titleCase(s))

आशा है कि स्पष्ट करता है।


11

मैं बस इसे जोड़ना चाहता था flatMap, आपको वास्तव में फ़ंक्शन के अंदर अपने स्वयं के कस्टम अवलोकन का उपयोग करने की आवश्यकता नहीं है और आप मानक कारखाने विधियों या ऑपरेटरों पर भरोसा कर सकते हैं:

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(final File file) {
        try {
            String json = new Gson().toJson(new FileReader(file), Object.class);
            return Observable.just(json);
        } catch (FileNotFoundException ex) {
            return Observable.<String>error(ex);
        }
    }
});

आम तौर पर, यदि संभव हो, तो आपको ऑन-एक्सएक्सएक्स विधियों और कॉलबैक से अपवाद (रनटाइम) अपवादों को फेंकने से बचना चाहिए, भले ही हम RxJava में जितने भी सुरक्षा उपाय कर सकें।


लेकिन मुझे लगता है कि नक्शा पर्याप्त है। तो फ़्लैटबॉल और नक्शा एक आदत है?
CoXier

6

उस परिदृश्य में मानचित्र का उपयोग करें, आपको इसके लिए एक नए अवलोकन की आवश्यकता नहीं है।

आपको Exception.propagate का उपयोग करना चाहिए, जो एक आवरण है ताकि आप उन अपवादों को आरएक्स तंत्र पर भेज सकें

Observable<String> obs = Observable.from(jsonFile).map(new Func1<File, String>() { 
    @Override public String call(File file) {
        try { 
            return new Gson().toJson(new FileReader(file), Object.class);
        } catch (FileNotFoundException e) {
            throw Exceptions.propagate(t); /will propagate it as error
        } 
    } 
});

फिर आपको ग्राहक में इस त्रुटि को संभालना चाहिए

obs.subscribe(new Subscriber<String>() {
    @Override 
    public void onNext(String s) { //valid result }

    @Override 
    public void onCompleted() { } 

    @Override 
    public void onError(Throwable e) { //e might be the FileNotFoundException you got }
};); 

इसके लिए एक उत्कृष्ट पोस्ट है: http://blog.danlew.net/2015/12/08/error-handling-in-rxxava/


0

कुछ मामलों में आप अंत में वेधशालाओं की श्रृंखला को समाप्त कर सकते हैं, जिसमें आपकी वेधशाला एक और अवलोकन योग्य होगी। 'फ्लैटमैप' प्रकार का दूसरा ऑब्जर्वेबल है जो पहले एक में दफन है और आपको सीधे डेटा का उपयोग करने देता है दूसरा ऑब्जर्वेबल सब्सक्राइब करते समय थूकता है।


0

Flatmap वेधशालाओं के लिए वेधशालाओं का मानचित्र बनाता है। आइटम के लिए मैप्स मैप करें।

फ्लैटमैप अधिक लचीला है, लेकिन मैप अधिक हल्का और सीधा है, इसलिए यह आपके usecase पर निर्भर करता है।

यदि आप कुछ भी कर रहे हैं तो एस्क्वायंट (स्विचिंग थ्रेड्स सहित), आपको फ्लैटमैप का उपयोग करना चाहिए, क्योंकि उपभोक्ता द्वारा निपटाए जाने पर मैप चेक नहीं करेगा (लाइटवेट-नेस का हिस्सा)

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