जावा 8 स्ट्रीम और RxJava वेधशालाओं के बीच अंतर


144

क्या Java 8 धाराएँ RxJava वेधशालाओं के समान हैं?

जावा 8 स्ट्रीम परिभाषा:

नए java.util.streamपैकेज में कक्षाएं तत्वों की धाराओं पर कार्यात्मक-शैली के संचालन का समर्थन करने के लिए एक स्ट्रीम एपीआई प्रदान करती हैं।


8
FYI करें JDK में कक्षाओं की तरह अधिक RxJava पेश करने का प्रस्ताव है 9. jsr166-concurrency.10961.n7.nabble.com/…
जॉन विंट

@JohnVint इस प्रस्ताव की स्थिति क्या है। क्या यह वास्तव में उड़ान लेगा?
इगोरगानापोलस्की

2
@IgorGanapolsky ओह हाँ, यह निश्चित रूप से लग रहा है कि यह jdk9 में कर देगा। cr.openjdk.java.net/~martin/webrevs/openjdk9/… । यहाँ तक कि RxJava से लेकर फ़्लो github.com/akarnokd/RxJavaUtilConcurrentFlow तक का पोर्ट है ।
जॉन विन

मुझे पता है कि यह वास्तव में एक पुराना सवाल है, लेकिन मैंने हाल ही में वेंकट सुब्रमण्यम की इस महान बातचीत में भाग लिया, जिसमें इस विषय पर एक व्यावहारिक जानकारी है और इसे Java9: youtube.com/watch?v=kfSSKM9y_0E से अपडेट किया गया है । RxJava में लोगों के लिए दिलचस्प हो सकता है।
पेड्रो

जवाबों:


152

टीएल; डीआर : सभी अनुक्रम / स्ट्रीम प्रोसेसिंग लिबास पाइपलाइन बिल्डिंग के लिए बहुत समान एपीआई की पेशकश कर रहे हैं। बहु-थ्रेडिंग और पाइपलाइनों की संरचना से निपटने के लिए एपीआई एपीआई में हैं।

RxJava, स्ट्रीम से काफी अलग है। सभी JDK चीजों में से, rx.Observable के सबसे करीब शायद java.util.stream.Collector Stream + CompletableFuture कॉम्बो है (जो अतिरिक्त मोनाड परत से निपटने की लागत पर आता है, यानी बीच में संभाल करने के लिए Stream<CompletableFuture<T>>और CompletableFuture<Stream<T>>)।

अवलोकन और धारा के बीच महत्वपूर्ण अंतर हैं:

  • धाराएँ पुल-आधारित हैं, वेधशालाएँ पुश-आधारित हैं। यह बहुत सार लग सकता है, लेकिन इसके महत्वपूर्ण परिणाम हैं जो बहुत ठोस हैं।
  • स्ट्रीम केवल एक बार उपयोग की जा सकती है, ऑब्जर्वेबल को कई बार सब्सक्राइब किया जा सकता है
  • Stream#parallel()विभाजन में अनुक्रम विभाजित करता है, Observable#subscribeOn()और Observable#observeOn()नहीं; Stream#parallel()ऑब्जर्वबल के साथ व्यवहार का अनुकरण करना मुश्किल है , यह एक बार .parallel()विधि थी लेकिन इस पद्धति ने इतना भ्रम पैदा किया कि .parallel()समर्थन को जीथब, आरएक्सजवापेरल पर अलग रिपॉजिटरी में स्थानांतरित कर दिया गया। अधिक विवरण एक और उत्तर में हैं
  • Stream#parallel()वैकल्पिक शेड्यूलर को स्वीकार करने वाले अधिकांश RxJava तरीकों के विपरीत, एक थ्रेड पूल का उपयोग करने की अनुमति नहीं देता है। चूंकि JVM में सभी स्ट्रीम इंस्टेंस एक ही कांटे-जुड़ने वाले पूल का उपयोग करते हैं, इसलिए .parallel()गलती से आपके प्रोग्राम के दूसरे मॉड्यूल में व्यवहार को प्रभावित कर सकते हैं
  • धाराओं में समय से संबंधित संचालन की कमी होती है Observable#interval(), जैसे Observable#window()और कई अन्य; यह ज्यादातर इसलिए है क्योंकि स्ट्रीम पुल-आधारित हैं, और अपस्ट्रीम का कोई नियंत्रण नहीं है कि अगले तत्व डाउनस्ट्रीम का उत्सर्जन कब करना है
  • RxJava की तुलना में धाराएँ संचालन के प्रतिबंधित सेट की पेशकश करती हैं। जैसे धाराओं में कट-ऑफ ऑपरेशन ( takeWhile(), takeUntil()) की कमी है ; वर्कअराउंड का उपयोग Stream#anyMatch()सीमित है: यह टर्मिनल ऑपरेशन है, इसलिए आप इसे प्रति स्ट्रीम एक से अधिक बार उपयोग नहीं कर सकते
  • JDK 8 के रूप में, कोई स्ट्रीम # ज़िप ऑपरेशन नहीं है, जो कभी-कभी काफी उपयोगी होता है
  • धाराओं का निर्माण अपने आप से कठिन है, ऑब्जर्वेबल का निर्माण कई तरीकों से किया जा सकता है EDIT: जैसा कि टिप्पणियों में उल्लेख किया गया है, स्ट्रीम के निर्माण के तरीके हैं। हालांकि, चूंकि कोई गैर-टर्मिनल शॉर्ट-सर्कुलेटिंग नहीं है, इसलिए आप फ़ाइल में लाइनों की स्ट्रीम को आसानी से उत्पन्न नहीं कर सकते हैं (JDK फाइलें # लाइनें और बफर लाइन प्रदान करता है # बॉक्स से बाहर # लाइनें, और अन्य समान परिदृश्यों को स्ट्रीम के निर्माण द्वारा प्रबंधित किया जा सकता है Iterator से)।
  • अवलोकन योग्य संसाधन प्रबंधन सुविधा प्रदान करता है ( Observable#using()); आप इसके साथ IO स्ट्रीम या म्यूटेक्स को लपेट सकते हैं और सुनिश्चित करें कि उपयोगकर्ता संसाधन को मुक्त करना नहीं भूलेंगे - यह सदस्यता समाप्ति पर स्वचालित रूप से निपटाया जाएगा; स्ट्रीम में onClose(Runnable)विधि है, लेकिन आपको इसे मैन्युअल रूप से या कोशिश-के-संसाधनों के माध्यम से कॉल करना होगा। ई। जी। आपको यह ध्यान रखना होगा कि फ़ाइलें # लाइनों () को प्रयास-से-संसाधन ब्लॉक में संलग्न होना चाहिए।
  • वेधशालाएं सभी तरह से सिंक्रनाइज़ की जाती हैं (मैं वास्तव में यह जांच नहीं करता था कि क्या स्ट्रीम के लिए भी यही सच है)। यह आपको यह सोचने से रोकता है कि क्या मूल संचालन थ्रेड-सुरक्षित है (उत्तर हमेशा 'हाँ' है, जब तक कि कोई बग न हो), लेकिन समसामयिक-संबंधित ओवरहेड वहाँ होगा, भले ही आपके कोड को इसकी आवश्यकता हो या नहीं।

राउंड-अप: RxJava स्ट्रीम से काफी अलग है। वास्तविक RxJava विकल्प रिएक्टिवस्ट्रीम के अन्य कार्यान्वयन हैं , जैसे अक्का का प्रासंगिक हिस्सा।

अद्यतन करें । गैर-डिफ़ॉल्ट कांटा-जुड़ने वाले पूल का उपयोग करने के लिए ट्रिक है Stream#parallel, जावा 8 समानांतर स्ट्रीम में कस्टम थ्रेड पूल देखें

अद्यतन करें । उपरोक्त सभी RxJava 1.x के अनुभव पर आधारित है। अब जब RxJava 2.x यहाँ है , तो यह उत्तर पुराना हो सकता है।


2
धाराओं का निर्माण क्यों मुश्किल है? इस लेख के अनुसार, यह आसान लगता है: oracle.com/technetwork/articles/java/…
IgGanapolsky

2
ऐसी कई कक्षाएं हैं जिनमें 'स्ट्रीम' पद्धति है: संग्रह, इनपुट स्ट्रीम, निर्देशिका फ़ाइलें, आदि। लेकिन क्या होगा यदि आप एक कस्टम लूप से एक स्ट्रीम बनाना चाहते हैं - कहते हैं, डेटाबेस कर्सर पर पुनरावृति? अब तक मैंने जो सबसे अच्छा तरीका पाया है, वह है इटरेटर बनाना, इसे स्प्लिटरेटर से लपेटना, और आखिर में स्ट्रीमसुपर से #SSSiteriter को इन्वाइट करना। एक साधारण मामले IMHO के लिए बहुत अधिक गोंद। Stream.iterate भी है लेकिन यह अनंत धारा पैदा करता है। उस मामले में चिल्लाहट काटने का एकमात्र तरीका स्ट्रीम # anyMatch है, लेकिन यह एक टर्मिनल ऑपरेशन है, इस प्रकार आप स्ट्रीम निर्माता और उपभोक्ता को अलग नहीं कर सकते
Kirill Gamazkov

2
RxJava में Observable.fromCallable, Observable.create और इतने पर हैं। या आप सुरक्षित रूप से अनंत वेधशाला का उत्पादन कर सकते हैं, फिर '.takeWhile (कंडीशन)' कहें, और आप उपभोक्ताओं को इस क्रम को शिपिंग के साथ ठीक कर रहे हैं
Kirill Gamazkov

1
धाराओं का निर्माण अपने आप से कठिन नहीं है। आप बस Stream.generate()अपने स्वयं के Supplier<U>कार्यान्वयन को कॉल और पास कर सकते हैं , बस एक सरल विधि जिससे आप स्ट्रीम में अगला आइटम प्रदान करते हैं। अन्य तरीकों के भार हैं। आसानी से एक अनुक्रम का निर्माण करने के लिए Streamजो पिछले मूल्यों पर निर्भर करता है आप interate()विधि का उपयोग कर सकते हैं , प्रत्येक Collectionमें एक stream()विधि है और एक varargs या सरणी से Stream.of()निर्माण करता Streamहै। अंत StreamSupportमें Spliterators का उपयोग करके या स्ट्रीम आदिम प्रकारों के लिए अधिक उन्नत स्ट्रीम निर्माण के लिए समर्थन है।
15

"धाराओं में कट-ऑफ ऑपरेशंस ( takeWhile(), takeUntil());" का अभाव है । - JDK9 में ये हैं, मुझे विश्वास है, टेकवाइल में () और ड्रॉपव्हील ()
अब्दुल

50

Java 8 स्ट्रीम और RxJava काफी समान दिखते हैं। उनके पास एक जैसे दिखने वाले ऑपरेटर (फ़िल्टर, मानचित्र, फ़्लैटमैप ...) हैं, लेकिन समान उपयोग के लिए नहीं बनाए गए हैं।

आप RxJava का उपयोग करके एसिंकोनस कार्य कर सकते हैं।

जावा 8 स्ट्रीम के साथ, आप अपने संग्रह की वस्तुओं को पार करेंगे।

आप RxJava (संग्रह के आइटम) में समान काम कर सकते हैं, लेकिन जैसे RxJava समवर्ती कार्य पर ध्यान केंद्रित किया जाता है, ..., यह सिंक्रनाइज़ेशन, कुंडी का उपयोग करता है, ... इसलिए RJJava का उपयोग करने वाला एक ही कार्य धीमा हो सकता है जावा 8 स्ट्रीम के साथ।

RxJava की तुलना की जा सकती है CompletableFuture, लेकिन यह केवल एक मूल्य से अधिक गणना करने में सक्षम हो सकता है।


12
यह ध्यान देने योग्य है कि स्ट्रीम ट्रावेल के बारे में आप एक गैर-समानांतर स्ट्रीम के लिए ही सही हैं। parallelStreamसरल ट्रैवर्सल्स / मैप्स / फ़िल्टरिंग आदि के समान तुल्यकालन का समर्थन करता है ..
जॉन विंट

2
मुझे नहीं लगता "RxJava का उपयोग करने वाला एक ही कार्य जावा 8 स्ट्रीम के साथ धीमा हो सकता है।" सार्वभौमिक रूप से सही रहता है, हाथ पर कार्य के आधार पर इसका भारी।
daschl

1
मुझे खुशी है कि आपने कहा कि RxJava का उपयोग करने वाला एक ही कार्य जावा 8 स्ट्रीम के साथ धीमा हो सकता है । यह एक बहुत महत्वपूर्ण अंतर है कि कई RxJava उपयोगकर्ताओं को इसके बारे में पता नहीं है।
इगोरगानापोलस्की

RxJava डिफ़ॉल्ट रूप से सिंक्रोनस है। क्या आपके पास अपने बयान का समर्थन करने के लिए कोई बेंचमार्क है कि यह धीमा हो सकता है?
मार्सिन कोज़ीस्की

6
@ marcin-koziński आप इस बेंचमार्क को देख सकते हैं: twitter.com/akarnokd/status/752465265091309568
dwursteisen

37

उदाहरण के लिए, कुछ तकनीकी और वैचारिक भिन्नताएं हैं, उदाहरण के लिए, जावा 8 स्ट्रीम एकल उपयोग, पुल आधारित, मूल्यों के समकालिक अनुक्रम हैं जबकि RxJava वेधशालाएँ पुन: अवलोकन योग्य, अनुकूली पुश-पुल आधारित, संभावित रूप से अतुल्यकालिक अनुक्रम मान हैं। RxJava का लक्ष्य जावा 6+ है और यह एंड्रॉइड पर भी काम करता है।


4
RxJava से जुड़े विशिष्ट कोड में लैम्ब्डा का भारी उपयोग होता है जो केवल जावा 8 पर उपलब्ध हैं। तो आप जावा 6 के साथ आरएक्स का उपयोग कर सकते हैं, लेकिन कोड शोर होगा
किरिल गमाज़कोव

1
एक समान अंतर यह है कि Rx वेधशाला अनिश्चित काल तक अनिश्चित काल तक जीवित रह सकती है। जावा 8 स्ट्रीम को डिफ़ॉल्ट रूप से संचालन के साथ समाप्त किया जाता है।
इगोरगानापोलस्की

2
@KirillGamazkov जावा को लक्ष्य बनाते समय आप अपने कोड को पूर्ववर्ती बनाने के लिए रेट्रोलंबा का उपयोग कर सकते हैं
Marcin Kozi 20ski


30

जावा 8 स्ट्रीम पुल आधारित हैं। आप प्रत्येक आइटम का उपभोग करने वाले जावा 8 स्ट्रीम पर पुनरावृति करते हैं। और यह एक अंतहीन धारा हो सकती है।

RXJava Observableडिफ़ॉल्ट पुश आधारित है। आप एक ऑब्जर्वेबल की सदस्यता लेते हैं और अगले आइटम के आने ( onNext), या जब धारा पूरी हो जाती है ( onCompleted), या जब एक त्रुटि हुई (तब) आपको सूचित किया जाएगा onError। क्योंकि के साथ Observableआपको प्राप्त onNext, onCompleted,onError घटनाओं, आप कुछ शक्तिशाली कार्यों अलग संयोजन की तरह कर सकते हैं Observableएक नया एक के लिए है ( zip, merge, concat)। अन्य सामान जो आप कर सकते थे, कैशिंग, थ्रॉटलिंग, ... और यह कमोबेश एक ही एपीआई का उपयोग विभिन्न भाषाओं में (RxJava, RX in C #, RxJS, ...) करता है।

डिफ़ॉल्ट रूप से RxJava सिंगल थ्रेडेड है। जब तक आप शेड्यूलर्स का उपयोग शुरू नहीं करते हैं, तब तक सब कुछ एक ही धागे पर होगा।


स्ट्रीम में आपके पास फॉरन है, जो कि onNext
paul

दरअसल, धाराएं आमतौर पर टर्मिनल होती हैं। "ऑपरेशन जो एक स्ट्रीम पाइपलाइन को बंद करते हैं, टर्मिनल ऑपरेशन कहलाते हैं। वे एक पाइपलाइन जैसे कि एक सूची, एक पूर्णांक, या यहां तक ​​कि शून्य (किसी भी गैर-स्ट्रीम प्रकार) के परिणामस्वरूप उत्पन्न होते हैं।" ~ oracle.com/technetwork/articles/java/…
इगोरगानापोलस्की

26

मौजूदा उत्तर व्यापक और सही हैं, लेकिन शुरुआती लोगों के लिए एक स्पष्ट उदाहरण की कमी है। मुझे "पुश / पुल-बेस्ड" और "री-ऑब्जर्वेबल" जैसे शब्दों के पीछे कुछ ठोस डालने की अनुमति दें। नोट : मैं पद से नफरत करता हूं Observable(यह स्वर्ग के लिए एक धारा है), इसलिए बस जे 8 बनाम आरएक्स धाराओं का उल्लेख करेगा।

पूर्णांकों की सूची पर विचार करें,

digits = [1,2,3,4,5]

एक J8 स्ट्रीम संग्रह को संशोधित करने के लिए एक उपयोगिता है। उदाहरण के लिए अंक भी निकाले जा सकते हैं,

evens = digits.stream().filter(x -> x%2).collect(Collectors.toList())

यह मूल रूप से पायथन का नक्शा, फ़िल्टर, कम करना है से जावा के अलावा , एक बहुत अच्छा (और लंबी अतिदेय) है। लेकिन क्या होगा अगर अंकों को समय से पहले एकत्र नहीं किया गया था - क्या होगा यदि एप्लिकेशन चलने के दौरान अंक स्ट्रीमिंग थे - क्या हम वास्तविक समय में भी फ़िल्टर कर सकते हैं।

एक अलग थ्रेड प्रक्रिया की कल्पना करें कि ऐप चल रहा है, जबकि यादृच्छिक समय पर पूर्णांक आउटपुट कर रहा है ( ---समय दर्शाता है)

digits = 12345---6------7--8--9-10--------11--12

आरएक्स में, प्रत्येक नए अंक पर प्रतिक्रियाeven कर सकते हैं और वास्तविक समय में फ़िल्टर लागू कर सकते हैं

even = -2-4-----6---------8----10------------12

इनपुट और आउटपुट सूचियों को संग्रहीत करने की कोई आवश्यकता नहीं है। यदि आप एक आउटपुट सूची चाहते हैं , तो कोई समस्या नहीं जो सुव्यवस्थित भी हो। वास्तव में, सब कुछ एक धारा है।

evens_stored = even.collect()  

यही कारण है कि "स्टेटलेस" और "फंक्शनल" जैसे शब्द आरएक्स से अधिक जुड़े हुए हैं


लेकिन 5 भी नहीं है ... और ऐसा लगता है कि जे 8 स्ट्रीम सिंक्रोनस है, जबकि आरएक्स स्ट्रीम अतुल्यकालिक है?
फ्रैंकलिन यू

1
@FranklinYu धन्यवाद मैंने 5 टाइपो तय किया। यदि सिंक्रोनस बनाम एसिंक्रोन के संदर्भ में कम सोचते हैं, हालांकि यह सही हो सकता है, और अनिवार्य बनाम कार्यात्मक के संदर्भ में अधिक हो सकता है। J8 में, आप पहले अपने सभी आइटम एकत्र करते हैं, फिर फ़िल्टर को दूसरे पर लागू करते हैं। आरएक्स में आप डेटा से स्वतंत्र फिल्टर फ़ंक्शन को परिभाषित करते हैं, और फिर इसे एक समान स्रोत (एक लाइव स्ट्रीम, या जावा संग्रह) के साथ जोड़ते हैं ... यह एक पूरी तरह से अलग प्रोग्रामिंग मॉडल है
एडम ह्यूजेस

मैं इससे बहुत हैरान हूं। मुझे पूरा यकीन है कि जावा स्ट्रीम को डेटा स्ट्रीमिंग से बनाया जा सकता है। आपको क्या लगता है कि इसके विपरीत क्या है?
विक सीडवेल्यू

4

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

जावा 8 धाराएँ एक अनबिके संग्रह के कार्यान्वयन के लिए बहुत ज्यादा हैं, जो कि स्काला स्ट्रीम या क्लोजर आलसी फक के समान है ।


3

जावा 8 स्ट्रीम मल्टीकोर आर्किटेक्चर का लाभ उठाते हुए वास्तव में बड़े संग्रह के प्रसंस्करण को सक्षम बनाता है। इसके विपरीत, RxJava डिफ़ॉल्ट रूप से (शेड्यूलर के बिना) थ्रेडेड है। इसलिए RxJava मल्टी-कोर मशीनों का लाभ नहीं उठाएगा जब तक कि आप उस तर्क को स्वयं कोड न करें।


4
स्ट्रीम डिफ़ॉल्ट रूप से अच्छी तरह से थ्रेडेड होती है, जब तक कि आप इनवोक नहीं करते हैं। समानांतर ()। साथ ही, Rx कंसीडर पर अधिक नियंत्रण देता है।
किरिल गमाज़कोव

@KirillGamazkov Kotlin Coroutines Flow (Java8 स्ट्रीम पर आधारित) अब संरचित संगामिति का समर्थन करता है: kotlinlang.org/docs/reference/coroutines/flow.html#flows
IgorGanapolsky

यह सच है, लेकिन मैंने फ्लो और स्ट्रक्चर्ड कंसीलर के बारे में कुछ नहीं कहा। मेरे दो बिंदु थे: 1) स्ट्रीम और आरएक्स दोनों एकल-थ्रेडेड हैं जब तक कि आप स्पष्ट रूप से इसे नहीं बदलते; 2) आरएक्स आपको ठीक-ठाक नियंत्रण देता है, जिस पर कदम थ्रेड पूल पर प्रदर्शन करने के लिए, धाराओं के विपरीत केवल आपको "इसे किसी तरह समानांतर बनाने" की अनुमति देता है
किरिल गमाज़कोव

मुझे वास्तव में इस सवाल का बिंदु नहीं मिला "आपको थ्रेड पूल की क्या आवश्यकता है"। जैसा कि आपने कहा है, "कुशलता से वास्तव में बड़े संग्रह के प्रसंस्करण को सक्षम करने के लिए"। या हो सकता है कि मैं अलग-अलग थ्रेड पूल पर काम करने के लिए IO- बाध्य भाग चाहता हूं। मुझे नहीं लगता कि मैंने आपके प्रश्न के पीछे के इरादे को समझा है। पुनः प्रयास करें?
किरिल गमाज़कोव

1
शेड्यूलर क्लास में स्टेटिक तरीके पूर्वनिर्धारित थ्रेड पूल प्राप्त करने के साथ-साथ एक्जिक्यूटर से एक बनाने की अनुमति देता है। देखें reactivex.io/RxJava/2.x/javadoc/io/reactivex/schedulers/...
किरिल Gamazkov
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.