2015-11-16 16 views
6

पर एक ऑपरेशन जोड़ें, मैं सोच रहा हूं कि स्ट्रीम के बाहर सेट की गई किसी प्रकार की स्थिति के आधार पर मैं स्ट्रीम में एक ऑपरेशन जोड़ सकता हूं या नहीं। उदाहरण के लिए, यदि मैं limit चर -1 के बराबर नहीं है, तो मैं स्ट्रीम में एक सीमा संचालन जोड़ना चाहता हूं।सशर्त रूप से जावा 8 स्ट्रीम

मेरे कोड वर्तमान में इस तरह दिखता है, लेकिन मैं अभी तक देखने के लिए नदियों के अन्य उदाहरण इस तरह से है, जहां एक स्ट्रीम वस्तु एक मध्यवर्ती आपरेशन पर ही लागू किया का परिणाम को सौंप रहा है इस्तेमाल किया जा रहा है:

// Do some stream stuff 
stream = stream.filter(e -> e.getTimestamp() < max); 

// Limit the stream 
if (limit != -1) { 
    stream = stream.limit(limit); 
} 

// Collect stream to list 
stream.collect(Collectors.toList()); 

जैसा कि इस stackoverflow post में बताया गया है, फ़िल्टर वास्तव में लागू नहीं होता है जब तक टर्मिनल ऑपरेशन नहीं कहा जाता है। चूंकि मैं टर्मिनल ऑपरेशन कहने से पहले स्ट्रीम के मूल्य को पुन: असाइन कर रहा हूं, क्या ऊपर दिए गए कोड अभी भी जावा 8 स्ट्रीम का उपयोग करने का एक उचित तरीका है?

+2

ऐसा लगता है कि आप 'stream.filter()' के आउटपुट को कैप्चर नहीं कर रहे हैं। – whaleberg

+1

यदि आप इसे एक पंक्ति में लिखना चाहते हैं, तो आप '.limit (सीमा! = -1? सीमा: Long.MAX_VALUE) लिख सकते हैं, लेकिन मैं नहीं करूँगा। – zapl

+0

@whaleberg, @WillShackleford, @ पीटर: मेरी गलती, मैं फ़िल्टर किए गए स्ट्रीम के मान को अपने मूल उदाहरण कोड में 'स्ट्रीम' पर असाइन करना भूल गया। मुझे लगता है कि मैं गलत तरीके से धाराओं को समझ गया। मैंने पढ़ा है कि सभी धाराओं को टर्मिनल ऑपरेशन के बाद निष्पादित किया जाना चाहिए ताकि उन्हें निष्पादित किया जा सके, इसलिए मैंने सोचा कि परिणामी धारा को उसी 'स्ट्रीम' चर में स्टोर करना गलत था। मैं अपना मूल प्रश्न अपडेट करूंगा। – Lani

उत्तर

10

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

a = object.foo(); 
b = a.bar(); 
c = b.baz(); 

और

c = object.foo().bar().baz(); 

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

फिर भी, एक स्ट्रीम के संदर्भ को तब तक स्टोर करना गलत नहीं है जब तक आप एक बार से अधिक संदर्भित संदर्भ का उपयोग न करने के अनुबंध का पालन करते हैं। इसका उपयोग करके वे आपके प्रश्न के अनुसार, यानी अगले आमंत्रण के परिणामस्वरूप चर को ओवरराइट कर रहे हैं, आप यह भी सुनिश्चित करते हैं कि आप एक लौटा संदर्भ पर एक से अधिक विधि का आह्वान नहीं करते हैं, इस प्रकार, यह एक सही उपयोग है।बेशक, यह केवल उसी प्रकार के मध्यवर्ती परिणामों के साथ काम करता है, इसलिए जब आप map या flatMap का उपयोग कर रहे हैं, तो एक अलग संदर्भ प्रकार की स्ट्रीम प्राप्त करने के बाद, आप स्थानीय चर को ओवरराइट नहीं कर सकते हैं। फिर आपको पुराने स्थानीय चर का फिर से उपयोग करने के लिए सावधान रहना होगा, लेकिन जैसा कि कहा गया है, जब तक आप अगले आमंत्रण के बाद इसका उपयोग नहीं कर रहे हैं, मध्यवर्ती भंडारण में कुछ भी गलत नहीं है।

कभी-कभी, में स्टोर करने के लिए, उदा।

try(Stream<String> stream = Files.lines(Paths.get("myFile.txt"))) { 
    stream.filter(s -> !s.isEmpty()).forEach(System.out::println); 
} 

ध्यान दें कि कोड निम्न विकल्पों के बराबर है:

try(Stream<String> stream = Files.lines(Paths.get("myFile.txt")).filter(s->!s.isEmpty())) { 
    stream.forEach(System.out::println); 
} 

और

try(Stream<String> srcStream = Files.lines(Paths.get("myFile.txt"))) { 
    Stream<String> tmp = srcStream.filter(s -> !s.isEmpty()); 
    // must not be use variable srcStream here: 
    tmp.forEach(System.out::println); 
} 

वे बराबर हैं क्योंकि forEach हमेशा filter का परिणाम जो हमेशा शुरू हो जाती है पर शुरू हो जाती है Files.lines के परिणामस्वरूप और इससे कोई फर्क नहीं पड़ता कि अंतिम close() ऑपरेशन का आह्वान किया गया है डी बंद होने से पूरी स्ट्रीम पाइपलाइन प्रभावित होती है।


इसे एक वाक्य में रखने के लिए, जिस तरह से आप इसका उपयोग करते हैं, वह सही है।


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

.limit(condition? aLimit: Long.MAX_VALUE) 

मानता है कि तत्वों की अधिकतम संख्या, क्या आपने कभी सामना कर सकते हैं, Long.MAX_VALUE लेकिन धाराओं कर सकते हैं उससे अधिक तत्व हैं, वे अनंत भी हो सकते हैं।

.limit(condition? aLimit: list.size()) 

जब स्ट्रीम स्रोत list, एक धारा के आलसी मूल्यांकन टूट रहा है। सिद्धांत रूप में, टर्मिनल एक्शन शुरू होने पर एक म्यूटेबल स्ट्रीम स्रोत कानूनी रूप से उस बिंदु तक मनमाने ढंग से बदल सकता है। नतीजा इस बिंदु तक किए गए सभी संशोधनों को प्रतिबिंबित करेगा। जब आप list.size() को शामिल करने वाले मध्यवर्ती ऑपरेशन को जोड़ते हैं, यानी इस बिंदु पर सूची का वास्तविक आकार, इस बिंदु और टर्मिनल ऑपरेशन के बीच संग्रह पर लागू किए गए संशोधनों में इस मान को "वास्तव में कोई सीमा" के मुकाबले अलग अर्थ नहीं हो सकता है अर्थ।

“Non Interference” section of the API documentation साथ तुलना करें:

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

List<String> l = new ArrayList(Arrays.asList("one", "two")); 
Stream<String> sl = l.stream(); 
l.add("three"); 
String s = sl.collect(joining(" ")); 

सबसे पहले एक सूची दो तारों से बना है: "एक"; और दो"। फिर उस सूची से एक धारा बनाई गई है। अगला सूची तीसरी स्ट्रिंग जोड़कर संशोधित की जाती है: "तीन"। अंत में धारा के तत्व एकत्र किए जाते हैं और एक साथ जुड़ जाते हैं। चूंकि टर्मिनल संग्रह ऑपरेशन शुरू होने से पहले सूची संशोधित की गई थी, परिणाम "एक दो तीन" की एक स्ट्रिंग होगी।

बेशक, यह सामान्य रूप से एक दुर्लभ कोने का मामला है, एक प्रोग्रामर स्रोत स्ट्रीम संग्रह को संशोधित किए बिना एक संपूर्ण स्ट्रीम पाइपलाइन तैयार करेगा। फिर भी, विभिन्न अर्थपूर्ण अवशेष हैं और जब आप एक बार कोने के मामले में प्रवेश करते हैं तो यह बग खोजने में बहुत मुश्किल हो सकता है।

आगे, चूंकि वे समकक्ष नहीं हैं, इसलिए स्ट्रीम एपीआई इन मानों को "वास्तव में कोई सीमा" के रूप में कभी नहीं पहचान पाएगी। यहां तक ​​कि Long.MAX_VALUE निर्दिष्ट करने का भी अर्थ है कि धारा कार्यान्वयन को सुनिश्चित करने के लिए संसाधित तत्वों की संख्या को ट्रैक करना है कि सीमा का पालन किया गया है। इस प्रकार, limit ऑपरेशन को जोड़ने से एक संख्या को सीमित करने पर एक महत्वपूर्ण प्रदर्शन लाभ हो सकता है जिसे प्रोग्रामर कभी पार नहीं होने की अपेक्षा करता है।

5

मुझे लगता है कि अपनी पहली लाइन होने की जरूरत है:

stream = stream.filter(e -> e.getTimestamp() < max); 

ताकि अपनी स्ट्रीम मूल धारा के बजाय बाद के संचालन में फिल्टर द्वारा वापस लौटाए का उपयोग कर।

5

आप दो तरह से कर सकते हैं इस

// Do some stream stuff 
List<E> results = list.stream() 
        .filter(e -> e.getTimestamp() < max); 
        .limit(limit > 0 ? limit : list.size()) 
        .collect(Collectors.toList()); 

या

// Do some stream stuff 
stream = stream.filter(e -> e.getTimestamp() < max); 

// Limit the stream 
if (limit != -1) { 
    stream = stream.limit(limit); 
} 

// Collect stream to list 
List<E> results = stream.collect(Collectors.toList()); 

इस कार्यात्मक प्रोग्रामिंग आप कभी भी प्रत्येक फ़ंक्शन के परिणाम पर काम करना चाहिए है। आपको प्रोग्रामिंग की इस शैली में कुछ भी संशोधित करने से बचाना चाहिए और सब कुछ ठीक करना चाहिए जैसे कि यदि संभव हो तो यह अपरिवर्तनीय था।

चूंकि मैं टर्मिनल ऑपरेशन कहने से पहले स्ट्रीम के मूल्य को फिर से सौंप रहा हूं, क्या उपर्युक्त कोड अभी भी जावा 8 स्ट्रीम का उपयोग करने का एक उचित तरीका है?

यह काम करना चाहिए, हालांकि यह अनिवार्य और कार्यात्मक कोडिंग के मिश्रण के रूप में पढ़ता है। मैं अपने पहले जवाब के अनुसार इसे एक निश्चित धारा के रूप में लिखने का सुझाव देता हूं।

+0

या '.limit (सीमा> 0? सीमा: Long.MAX_VALUE) 'यदि स्ट्रीम संग्रह पर आधारित नहीं है। – zeroflagL

+0

@ होल्गर क्या गलत था कि एक प्रतिलिपि ने प्रश्न से कोड चिपकाया और टिप्पणी को मिटाना भूल गया। इसे लेने के लिए धन्यवाद। –

संबंधित मुद्दे