इंटरमीडिएट स्ट्रीम ऑपरेशन का मूल्यांकन गणना पर नहीं किया गया


33

ऐसा लगता है कि मुझे यह समझने में परेशानी हो रही है कि जावा कैसे एक स्ट्रीम पाइपलाइन में धारा संचालन की रचना करता है।

निम्नलिखित कोड निष्पादित करते समय

public
 static void main(String[] args) {
    StringBuilder sb = new StringBuilder();

    var count = Stream.of(new String[]{"1", "2", "3", "4"})
            .map(sb::append)
            .count();

    System.out.println(count);
    System.out.println(sb.toString());
}

कंसोल केवल प्रिंट करता है 4StringBuilderवस्तु अभी भी महत्व है ""

जब मैं फ़िल्टर ऑपरेशन जोड़ता हूं: filter(s -> true)

public static void main(String[] args) {
    StringBuilder sb = new StringBuilder();

    var count = Stream.of(new String[]{"1", "2", "3", "4"})
            .filter(s -> true)
            .map(sb::append)
            .count();

    System.out.println(count);
    System.out.println(sb.toString());
}

आउटपुट में परिवर्तन होता है:

4
1234

यह प्रतीत होता है कि निरर्थक फ़िल्टर ऑपरेशन कैसे निर्मित धारा पाइपलाइन के व्यवहार को बदलता है?


2
दिलचस्प !!!
uneq95

3
मुझे लगता है कि यह कार्यान्वयन-विशिष्ट व्यवहार है; शायद ऐसा इसलिए है क्योंकि पहली धारा में एक ज्ञात आकार है, लेकिन दूसरा नहीं है और आकार-नेस यह निर्धारित करता है कि मध्यवर्ती संचालन निष्पादित किया गया है या नहीं।
एंडी टर्नर

ब्याज से बाहर, यदि आप फ़िल्टर और मानचित्र को उल्टा करते हैं तो क्या होता है?
एंडी टर्नर

हास्केल में थोड़ा सा प्रोग्राम करने के बाद, यह कुछ आलसी मूल्यांकन की तरह बदबू आ रही है। एक Google खोज लौटी, कि धाराओं में वास्तव में कुछ आलस्य है। मामला है कि हो सकता है? और एक फिल्टर के बिना, यदि जावा पर्याप्त चालाक है, तो वास्तव में मानचित्रण को निष्पादित करने की कोई आवश्यकता नहीं है।
फ्रेडरिक

@AndyTurner यह एक ही परिणाम देता है, यहां तक ​​कि उलटा भी
uneq95

जवाबों:


39

count()टर्मिनल आपरेशन, JDK के अपने संस्करण में, निम्न कोड को क्रियान्वित करने समाप्त होता है:

if (StreamOpFlag.SIZED.isKnown(helper.getStreamAndOpFlags()))
    return spliterator.getExactSizeIfKnown();
return super.evaluateSequential(helper, spliterator);

यदि filter()ऑपरेशन की पाइपलाइन में एक ऑपरेशन होता है , तो स्ट्रीम का आकार, जिसे शुरू में जाना जाता है, अब और नहीं जाना जा सकता है (क्योंकि filterस्ट्रीम के कुछ तत्वों को अस्वीकार कर सकता है)। तो ifब्लॉक निष्पादित नहीं किया जाता है, मध्यवर्ती संचालन निष्पादित किए जाते हैं और स्ट्रिंगब्यूलर को इस प्रकार संशोधित किया जाता है।

दूसरी ओर, यदि आपके पास केवल map()पाइप लाइन है, तो स्ट्रीम में तत्वों की संख्या प्रारंभिक तत्वों की संख्या के समान होने की गारंटी है। तो अगर ब्लॉक निष्पादित किया जाता है, और आकार सीधे मध्यवर्ती संचालन का मूल्यांकन किए बिना वापस आ जाता है।

ध्यान दें कि लैंबडा map()दस्तावेज में परिभाषित अनुबंध का उल्लंघन करने के लिए पारित किया गया है: यह एक गैर-हस्तक्षेप, स्टेटलेस ऑपरेशन माना जाता है, लेकिन यह स्टेटलेस नहीं है। इसलिए दोनों मामलों में एक अलग परिणाम होने को बग के रूप में नहीं माना जा सकता है।


क्योंकि flatMap()तत्वों की संख्या को बदलने में सक्षम हो सकता है, यही कारण था कि शुरू में उत्सुक था (अब आलसी)? इसलिए, forEach()यदि map()इसका मौजूदा स्वरूप अनुबंध का उल्लंघन करता है , तो विकल्प का उपयोग और गणना अलग से की जाएगी ।
फ्रेडरिक

3
फ्लैटपाइप के बारे में, मुझे नहीं लगता। यह AFAIK था, क्योंकि इसे उत्सुक बनाने के लिए सरल प्रारंभिक था। हां, साइड इफेक्ट्स का उत्पादन करने के लिए, नक्शे के साथ एक धारा का उपयोग करना एक बुरा विचार है।
जेबी निजेट

क्या आपके पास यह सुझाव होगा 4 1234कि नक्शे में अतिरिक्त फ़िल्टर या उत्पादन के साइड इफेक्ट्स का उपयोग किए बिना पूर्ण आउटपुट कैसे प्राप्त करें ?
अटलान्टस

1
int count = array.length; String result = String.join("", array);
जेबी निजेट

1
या आप forEach का उपयोग कर सकते हैं यदि आप वास्तव में StringBuilder का उपयोग करना चाहते हैं, या आप उपयोग कर सकते हैंCollectors.joining("")
njzk2

19

में JDK-9 यह स्पष्ट रूप से जावा डॉक्स में प्रलेखित किया गया

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

एपीआई नोट:

एक कार्यान्वयन धारा पाइपलाइन (या तो क्रमिक या समानांतर में) को निष्पादित नहीं करने का चयन कर सकता है यदि यह धारा स्रोत से सीधे गणना को सक्षम करने में सक्षम है। ऐसे मामलों में किसी भी स्रोत तत्वों का पता नहीं लगाया जाएगा और किसी भी मध्यवर्ती संचालन का मूल्यांकन नहीं किया जाएगा। साइड-इफेक्ट के साथ व्यवहार पैरामीटर, जो डिबगिंग जैसे हानिरहित मामलों को छोड़कर दृढ़ता से हतोत्साहित करते हैं, प्रभावित हो सकते हैं। उदाहरण के लिए, निम्नलिखित स्ट्रीम पर विचार करें:

 List<String> l = Arrays.asList("A", "B", "C", "D");
 long count = l.stream().peek(System.out::println).count();

स्ट्रीम स्रोत द्वारा कवर किए गए तत्वों की संख्या, एक सूची, ज्ञात है और मध्यवर्ती संचालन, झांकना, स्ट्रीम से तत्वों को इंजेक्ट या निकालना नहीं है (जैसा कि फ्लैटपाइप या फ़िल्टर संचालन के लिए मामला हो सकता है)। इस प्रकार गिनती सूची का आकार है और पाइपलाइन को निष्पादित करने की कोई आवश्यकता नहीं है और, साइड-इफेक्ट के रूप में, सूची तत्वों को प्रिंट करें।


0

यह वह नहीं है जो .map के लिए है। इसका उपयोग "समथिंग" की धारा को "समथिंग एल्स" की धारा में बदलने के लिए किया जाना चाहिए। इस स्थिति में, आप बाहरी स्ट्रिंगर के लिए स्ट्रिंग को मैप करने के लिए मैप का उपयोग कर रहे हैं, जिसके बाद आपके पास "स्ट्रिंगरबिल्डर" की एक धारा है, जिसमें से प्रत्येक को मैप ऑपरेशन द्वारा मूल स्ट्रिंगर के एक नंबर को जोड़कर बनाया गया था।

आपका स्ट्रीम वास्तव में स्ट्रीम में मैप किए गए परिणामों के साथ कुछ नहीं करता है, इसलिए यह मान लेना पूरी तरह से उचित है कि स्ट्रीम प्रोसेसर द्वारा कदम को छोड़ दिया जा सकता है। आप कार्य करने के लिए दुष्प्रभावों पर भरोसा कर रहे हैं, जो मानचित्र के कार्यात्मक मॉडल को तोड़ता है। ऐसा करने के लिए forEach का उपयोग करके आपको बेहतर सेवा दी जाएगी। पूरी तरह से एक अलग स्ट्रीम के रूप में गिनती करें, या forEach में AtomicInt का उपयोग करके एक काउंटर लगाएं।

फ़िल्टर इसे स्ट्रीम कंटेंट को चलाने के लिए मजबूर करता है क्योंकि इसे अब प्रत्येक स्ट्रीम एलिमेंट के साथ कुछ सार्थक करना है।

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