"स्ट्रीम पहले से ही या बंद हो चुकी है" से बचने के लिए एक स्ट्रीम कॉपी करें


121

मैं एक जावा 8 स्ट्रीम को डुप्लिकेट करना चाहूंगा ताकि मैं इससे दो बार निपट सकूं। मैं collectएक सूची के रूप में और उस से नई धाराएँ प्राप्त कर सकता हूं ;

// doSomething() returns a stream
List<A> thing = doSomething().collect(toList());
thing.stream()... // do stuff
thing.stream()... // do other stuff

लेकिन मुझे लगता है कि एक अधिक कुशल / सुरुचिपूर्ण तरीका होना चाहिए।

क्या किसी संग्रह में बदलकर धारा को कॉपी करने का कोई तरीका है?

मैं वास्तव में Eitherएस की एक धारा के साथ काम कर रहा हूं , इसलिए सही प्रक्षेपण पर जाने से पहले बाएं प्रक्षेपण को एक तरीके से संसाधित करना चाहता हूं और उस दूसरे तरीके से निपटना चाहता हूं। इस तरह की तरह (जो, अब तक, मैं toListचाल के साथ उपयोग करने के लिए मजबूर हूं )।

List<Either<Pair<A, Throwable>, A>> results = doSomething().collect(toList());

Stream<Pair<A, Throwable>> failures = results.stream().flatMap(either -> either.left());
failures.forEach(failure -> ... );

Stream<A> successes = results.stream().flatMap(either -> either.right());
successes.forEach(success -> ... );

क्या आप "प्रक्रिया एक तरह से" पर अधिक विस्तृत कर सकते हैं ... क्या आप वस्तुओं का उपभोग कर रहे हैं? उनका मानचित्रण? partBy () और groupingBy () आपको सीधे 2+ सूचियों में मिल सकते हैं, लेकिन आपको पहले मैपिंग से लाभ हो सकता है या आपके forEach () में केवल एक निर्णय कांटा होने का फायदा हो सकता है।
AjahnCharles

कुछ मामलों में, इसे एक संग्रह में बदलना एक विकल्प नहीं हो सकता है अगर हम अनंत धारा के साथ काम कर रहे हैं। आपको यहाँ पर संस्मरण का विकल्प मिल सकता है: dzone.com/articles/how-to-replay-java-streams
मिगुएल गैंबो

जवाबों:


88

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

यदि आप एक ही डेटा को फिर से उपयोग करना चाहते हैं, तो परिभाषा के अनुसार या तो आपको इसे दो बार (निर्धारक रूप से) उत्पन्न करना होगा या इसे स्टोर करना होगा। अगर यह पहले से ही एक संग्रह में होने के लिए होता है, महान; तो इसे दो बार पुनरावृत्त करना सस्ता है।

हमने "फोर्कड स्ट्रीम" के साथ डिजाइन में प्रयोग किया। हमने जो पाया वह यह था कि इसकी वास्तविक लागत का समर्थन करना; यह असामान्य मामले की कीमत पर आम मामले (एक बार उपयोग) पर बोझ डाल दिया। बड़ी समस्या यह थी कि "क्या होता है जब दो पाइपलाइन एक ही दर पर डेटा का उपभोग नहीं करते हैं।" अब आप किसी भी तरह बफ़र कर रहे हैं। यह एक ऐसी विशेषता थी जो स्पष्ट रूप से अपना वजन नहीं उठाती थी।

यदि आप एक ही डेटा को बार-बार ऑपरेट करना चाहते हैं, तो इसे स्टोर करें, या कंज्यूमर्स के रूप में अपने ऑपरेशंस को स्ट्रक्चर करें और निम्न कार्य करें:

stream()...stuff....forEach(e -> { consumerA(e); consumerB(e); });

आप RxJava लाइब्रेरी में भी देख सकते हैं, क्योंकि इसका प्रोसेसिंग मॉडल इस तरह के "स्ट्रीम फोर्किंग" के लिए बेहतर है।


1
शायद मुझे "दक्षता" का उपयोग नहीं करना चाहिए था, मैं इस तरह से प्राप्त कर रहा हूं कि मैं धाराओं के साथ क्यों परेशान होऊंगा (और कुछ भी स्टोर नहीं कर सकता हूं) यदि मैं जो भी करता हूं वह तुरंत डेटा ( toList) को स्टोर करने में सक्षम हो (इसे Eitherमामले में) उदाहरण है)?
टॉबी

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

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

@NiallConnaughton मुझे यकीन नहीं है कि आपकी बात चाहते हैं। यदि आप इसे दो बार पार करना चाहते हैं, तो किसी को इसे स्टोर करना होगा, या आपको इसे फिर से बनाना होगा। क्या आप यह सुझाव दे रहे हैं कि पुस्तकालय को केवल दो बार इसकी आवश्यकता होने पर इसे बफर करना चाहिए? वह मूर्खतापूर्ण होगा।
ब्रायन गोएटज

यह सुझाव देने के लिए कि पुस्तकालय को इसे बफर करना चाहिए, लेकिन यह कहना कि धाराएँ एक-धारा के रूप में होने के कारण, यह उन लोगों को मजबूर करता है जो एक बीज प्रवाह का पुन: उपयोग करना चाहते हैं (यानी: इसे परिभाषित करने के लिए उपयोग किए गए घोषणात्मक तर्क को साझा करना) या तो एकत्रित करने के लिए कई व्युत्पन्न धाराएँ बनाना बीज स्ट्रीम, या एक प्रदाता कारखाने तक पहुंच है जो बीज धारा का एक डुप्लिकेट बनाएगा। दोनों विकल्पों में उनके दर्द बिंदु हैं। इस उत्तर के विषय पर बहुत अधिक विवरण है: stackoverflow.com/a/28513908/114200
निल कनॉटघटन

73

आप Supplierस्ट्रीम पाइपलाइन के सामान्य भागों को सेट करने के लिए स्थानीय चर का उपयोग कर सकते हैं ।

से http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ :

पुन: उपयोग की धाराएँ

जावा 8 धाराओं का पुन: उपयोग नहीं किया जा सकता है। जैसे ही आप किसी भी टर्मिनल ऑपरेशन को कॉल करते हैं, स्ट्रीम बंद हो जाती है:

Stream<String> stream = Stream.of("d2", "a2", "b1", "b3", "c")
    .filter(s -> s.startsWith("a"));
stream.anyMatch(s -> true);    // ok
stream.noneMatch(s -> true);   // exception

Calling `noneMatch` after `anyMatch` on the same stream results in the following exception:
java.lang.IllegalStateException: stream has already been operated upon or closed
at 
java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at 
java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)
at com.winterbe.java8.Streams5.test7(Streams5.java:38)
at com.winterbe.java8.Streams5.main(Streams5.java:28)

इस सीमा को पार करने के लिए हमें हर उस टर्मिनल ऑपरेशन के लिए एक नई स्ट्रीम चेन बनानी होगी, जिसे हम निष्पादित करना चाहते हैं, जैसे हम पहले से स्थापित सभी इंटरमीडिएट ऑपरेशंस के साथ एक नई स्ट्रीम बनाने के लिए स्ट्रीम सप्लायर बना सकते हैं:

Supplier<Stream<String>> streamSupplier =
    () -> Stream.of("d2", "a2", "b1", "b3", "c")
            .filter(s -> s.startsWith("a"));

streamSupplier.get().anyMatch(s -> true);   // ok
streamSupplier.get().noneMatch(s -> true);  // ok

प्रत्येक कॉल get()एक नई स्ट्रीम का निर्माण करती है जिस पर हम वांछित टर्मिनल ऑपरेशन को कॉल करने के लिए सहेजते हैं।


2
अच्छा और सुरुचिपूर्ण समाधान। सबसे अधिक हल किए गए समाधान की तुलना में बहुत अधिक java8-ish।
डायलायनाटो

Supplierयदि Stream"महंगा" तरीके से बनाया गया है, तो उपयोग करने पर एक नोट , आप प्रत्येक कॉल के लिए उस लागत का भुगतान करते हैंSupplier.get() । यानी अगर एक डेटाबेस क्वेरी ... उस क्वेरी को हर बार किया जाता है
जुलिएन

आप एक IntStream का उपयोग करते हुए एक MapTo के बाद इस पैटर्न का पालन नहीं कर सकते। मैंने पाया कि मुझे इसे वापस एक Set<Integer>प्रयोग में बदलना है collect(Collectors.toSet())... और उस पर एक-दो ऑपरेशन करना है। मैं चाहता था max()और अगर एक विशिष्ट मूल्य दो ऑपरेशन के रूप में सेट किया गया था ...filter(d -> d == -1).count() == 1;
JGFMK

16

Supplierप्रत्येक समाप्ति ऑपरेशन के लिए स्ट्रीम का उत्पादन करने के लिए एक का उपयोग करें ।

Supplier<Stream<Integer>> streamSupplier = () -> list.stream();

जब भी आपको उस संग्रह की एक धारा की आवश्यकता streamSupplier.get()हो, एक नई धारा प्राप्त करने के लिए उपयोग करें।

उदाहरण:

  1. streamSupplier.get().anyMatch(predicate);
  2. streamSupplier.get().allMatch(predicate2);

आप के रूप में आप को इंगित कर रहे हैं कि आप पहली बार आपूर्तिकर्ताओं को यहाँ बताया है।
EnzoBnl

9

हमने jOOλ , ओपन सोर्स लाइब्रेरी duplicate()में स्ट्रीम के लिए एक विधि लागू की है जिसे हमने jOOQ के लिए एकीकरण परीक्षण में सुधार के लिए बनाया है । अनिवार्य रूप से, आप बस लिख सकते हैं:

Tuple2<Seq<A>, Seq<A>> duplicates = Seq.seq(doSomething()).duplicate();

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

यहाँ बताया गया है कि एल्गोरिथ्म कैसे काम करता है:

static <T> Tuple2<Seq<T>, Seq<T>> duplicate(Stream<T> stream) {
    final List<T> gap = new LinkedList<>();
    final Iterator<T> it = stream.iterator();

    @SuppressWarnings("unchecked")
    final Iterator<T>[] ahead = new Iterator[] { null };

    class Duplicate implements Iterator<T> {
        @Override
        public boolean hasNext() {
            if (ahead[0] == null || ahead[0] == this)
                return it.hasNext();

            return !gap.isEmpty();
        }

        @Override
        public T next() {
            if (ahead[0] == null)
                ahead[0] = this;

            if (ahead[0] == this) {
                T value = it.next();
                gap.offer(value);
                return value;
            }

            return gap.poll();
        }
    }

    return tuple(seq(new Duplicate()), seq(new Duplicate()));
}

अधिक स्रोत कोड यहाँ

Tuple2शायद अपनी तरह है Pair, प्रकार, जबकि Seqहै Streamकुछ संवर्द्धन के साथ।


2
यह समाधान थ्रेड-सुरक्षित नहीं है: आप किसी एक स्ट्रीम को दूसरे थ्रेड में पारित नहीं कर सकते। मुझे वास्तव में कोई परिदृश्य नहीं दिखता है जब दोनों धाराओं को एकल धागे में समान दर से खाया जा सकता है और आपको वास्तव में दो अलग-अलग धाराओं की आवश्यकता होती है। यदि आप एक ही स्ट्रीम से दो परिणाम तैयार करना चाहते हैं, तो कलेक्टरों के संयोजन का उपयोग करना बेहतर होगा (जो कि आपके पास पहले से ही है)।
टैगिर वलेव

@TagirValeev: आप थ्रेड-सुरक्षा, अच्छे बिंदु के बारे में सही हैं। कलेक्टरों के संयोजन के साथ यह कैसे किया जा सकता है?
लुकास एडर

1
मेरा मतलब है कि अगर कोई एक ही स्ट्रीम को दो बार इस तरह इस्तेमाल करना चाहता है Tuple2<Seq<A>>, Seq<A>> t = duplicate(stream); long count = t.collect(counting()); List<A> list = t.collect(toList());, तो यह बेहतर है Tuple2<Long, List<A>> t = stream.collect(Tuple.collectors(counting(), toList()));Collectors.mapping/reducingएक के प्रयोग से अन्य स्ट्रीम ऑपरेशन को कलेक्टर और प्रोसेस एलिमेंट के रूप में व्यक्त किया जा सकता है, जो एकल परिणामी ट्यूपल का निर्माण करता है। तो सामान्य तौर पर आप नकल के बिना एक बार धारा का उपभोग करने वाले कई काम कर सकते हैं और यह समानांतर-अनुकूल होगा।
टैगिर वलेव

2
इस मामले में आप एक के बाद एक धारा को कम करते जाएंगे। तो जीवन को कठिन बनाने के लिए ऐसा कोई मतलब नहीं है जो परिष्कृत इटोमरेटर को पेश करता है, जो वैसे भी हुड के तहत पूरी स्ट्रीम को सूची में एकत्र करेगा। आप केवल सूची में स्पष्ट रूप से एकत्र कर सकते हैं, फिर उसमें से दो धाराएँ बना सकते हैं जैसे कि ओपी बताता है (यह कोड लाइनों की समान संख्या है)। ठीक है, आप केवल कुछ सुधार कर सकते हैं यदि पहली कमी शॉर्ट-सर्किट है, लेकिन यह ओपी मामला नहीं है।
टैगिर वलेव

1
@ ओमार्टिनस: धन्यवाद, अच्छा सूचक। मैंने बेंचमार्क के लिए एक मुद्दा बनाया है । मैंने इसे offer()/ poll()API के लिए उपयोग किया था , लेकिन ArrayDequeशायद ऐसा ही हो।
लुकास एडर

7

आप रनवे की एक धारा बना सकते हैं (उदाहरण के लिए):

results.stream()
    .flatMap(either -> Stream.<Runnable> of(
            () -> failure(either.left()),
            () -> success(either.right())))
    .forEach(Runnable::run);

आवेदन कहां failureऔर successकैसे करने हैं। हालांकि यह काफी कुछ अस्थायी वस्तुओं का निर्माण करेगा और एक संग्रह से शुरू करने और इसे दो बार स्ट्रीमिंग / स्ट्रीमिंग से अधिक कुशल नहीं हो सकता है।


4

कई बार तत्वों को संभालने का एक और तरीका Stream.peek (उपभोक्ता) का उपयोग करना है :

doSomething().stream()
.peek(either -> handleFailure(either.left()))
.foreach(either -> handleSuccess(either.right()));

peek(Consumer) जरूरत के रूप में कई बार जंजीर किया जा सकता है।

doSomething().stream()
.peek(element -> handleFoo(element.foo()))
.peek(element -> handleBar(element.bar()))
.peek(element -> handleBaz(element.baz()))
.foreach(element-> handleQux(element.qux()));

ऐसा लगता है कि इस के लिए इस्तेमाल होने वाली झलक नहीं है (देखें सॉफ़्टवेयरइंजीनियरिंग.स्टैकएक्सचेंज.
com

2
@ हेक्टरज अन्य सूत्र तत्वों को संशोधित करने के बारे में है। मैंने मान लिया कि यहाँ नहीं किया जाता है।
मार्टिन

2

साइक्लोप्स-रिएक्ट , एक पुस्तकालय, जिसमें मैं योगदान देता हूं, में एक स्टैटिक तरीका है जो आपको स्ट्रीम को डुप्लिकेट करने की अनुमति देगा (और धाराओं के एक जूलर ट्यूपल लौटाता है)।

    Stream<Integer> stream = Stream.of(1,2,3);
    Tuple2<Stream<Integer>,Stream<Integer>> streams =  StreamUtils.duplicate(stream);

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

एक (आलसी) स्ट्रीम करने योग्य वर्ग भी है जिसे एक स्ट्रीम, Iterable या Array से बनाया जा सकता है और कई बार दोहराया जा सकता है।

    Streamable<Integer> streamable = Streamable.of(1,2,3);
    streamable.stream().forEach(System.out::println);
    streamable.stream().forEach(System.out::println);

AsStreamable.synchronizedFromStream (स्ट्रीम) - एक स्ट्रीमएबल बनाने के लिए इस्तेमाल किया जा सकता है जो आलसी रूप से बैकिंग संग्रह को पॉप्युलेट करेगा, एक तरह से जिसे थ्रेड्स में साझा किया जा सकता है। Streamable.fromStream (स्ट्रीम) किसी भी सिंक्रनाइज़ेशन ओवरहेड को लाइक नहीं करेगा।


2
और, निश्चित रूप से यह ध्यान दिया जाना चाहिए कि परिणामी धाराओं में महत्वपूर्ण सीपीयू / मेमोरी ओवरहेड और बहुत खराब समानांतर प्रदर्शन है। इसके अलावा यह समाधान थ्रेड-सुरक्षित नहीं है (आप परिणामी धाराओं में से किसी एक को दूसरे थ्रेड में पारित नहीं कर सकते हैं और इसे समानांतर रूप से सुरक्षित रूप से संसाधित कर सकते हैं)। यह बहुत अधिक प्रदर्शनकारी और सुरक्षित होगा List<Integer> list = stream.collect(Collectors.toList()); streams = new Tuple2<>(list.stream(), list.stream())(जैसा कि ओपी सुझाव देता है)। कृपया जवाब में स्पष्ट रूप से प्रकट करें कि आप साइक्लोप-धाराओं के लेखक हैं। पढ़ें यह
टैगिर वलेव

प्रतिबिंबित करने के लिए अद्यतन मैं लेखक हूँ। साथ ही प्रत्येक के प्रदर्शन charachteristics पर चर्चा करने के लिए एक अच्छा बिंदु। ऊपर दिए गए आपके मूल्यांकन में स्ट्रीमयूटिल्स.डुप्लिकेट के लिए बहुत अधिक स्थान है। StreamUtils.dupate एक सीपीयू और मेमोरी ओवरहेड (उपयोग केस के आधार पर) दोनों के लिए, एक स्ट्रीम से दूसरे में डेटा को बफर करके काम करता है। Streamable.of (1,2,3) के लिए, हालांकि, हर बार सीधे ऐरे से एक नई स्ट्रीम बनाई जाती है और प्रदर्शन विशेषताओं, जिसमें समानांतर प्रदर्शन शामिल है, सामान्य रूप से बनाए गए स्ट्रीम के लिए समान होगी।
जॉन मैक्लेन

इसके अलावा, एक AsStreamable वर्ग है जो स्ट्रीम से एक स्ट्रीम करने योग्य इंस्टेंस के निर्माण की अनुमति देता है, लेकिन स्ट्रीम करने योग्य बैकिंग संग्रह तक पहुंच को सिंक्रनाइज़ करता है क्योंकि यह बनाया गया है (AsStreamable.synchronizedFromStream)। इसे थ्रेड्स में उपयोग करने के लिए और अधिक उपयुक्त बनाना (यदि वह है जो आपको चाहिए - मुझे लगता है कि 99% समय स्ट्रीम एक ही थ्रेड पर बनाए और पुन: उपयोग किए जाते हैं)।
जॉन मैक्लेन

हाय टैगिर - क्या आपको अपनी टिप्पणी में यह भी नहीं बताना चाहिए कि आप एक प्रतिस्पर्धी पुस्तकालय के लेखक हैं?
जॉन मैक्लेन

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

0

इस विशेष समस्या के लिए आप विभाजन का भी उपयोग कर सकते हैं। कुछ इस तरह

     // Partition Eighters into left and right
     List<Either<Pair<A, Throwable>, A>> results = doSomething();
     Map<Boolean, Object> passingFailing = results.collect(Collectors.partitioningBy(s -> s.isLeft()));
     passingFailing.get(true) <- here will be all passing (left values)
     passingFailing.get(false) <- here will be all failing (right values)

0

स्ट्रीम बनाने या पढ़ने के समय हम स्ट्रीम बिल्डर का उपयोग कर सकते हैं। यहाँ स्ट्रीम बिल्डर का दस्तावेज़ है ।

https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.Builder.html

उदाहरण

मान लें कि हमारे पास कर्मचारी स्ट्रीम है और हमें एक्सेल फ़ाइल में कर्मचारी डेटा लिखने के लिए इस स्ट्रीम का उपयोग करने की आवश्यकता है और फिर कर्मचारी संग्रह / तालिका को अपडेट करें [यह स्ट्रीम बिल्डर का उपयोग दिखाने के लिए केवल उपयोग का मामला है]:

Stream.Builder<Employee> builder = Stream.builder();

employee.forEach( emp -> {
   //store employee data to excel file 
   // and use the same object to build the stream.
   builder.add(emp);
});

//Now this stream can be used to update the employee collection
Stream<Employee> newStream = builder.build();

0

मेरे पास एक समान समस्या थी, और तीन अलग-अलग मध्यवर्ती संरचनाओं के बारे में सोच सकता था जिसमें से धारा की एक प्रति बनाने के लिए: ए List, एक सरणी और एक Stream.Builder। मैंने थोड़ा बेंचमार्क प्रोग्राम लिखा, जिसमें बताया गया कि प्रदर्शन के दृष्टिकोण Listसे अन्य दो की तुलना में लगभग 30% धीमा था जो काफी समान थे।

किसी सरणी में कनवर्ट करने का एकमात्र दोष यह है कि यदि आपका तत्व प्रकार एक सामान्य प्रकार है (जो मेरे मामले में यह है); इसलिए मैं एक का उपयोग करना पसंद करते हैं Stream.Builder

मैंने एक छोटे से फंक्शन को लिखना शुरू किया जो एक बनाता है Collector:

private static <T> Collector<T, Stream.Builder<T>, Stream<T>> copyCollector()
{
    return Collector.of(Stream::builder, Stream.Builder::add, (b1, b2) -> {
        b2.build().forEach(b1);
        return b1;
    }, Stream.Builder::build);
}

मैं तो किसी भी स्ट्रीम के प्रति बना सकते हैं strऐसा करने से str.collect(copyCollector())जो काफी धाराओं का मुहावरेदार उपयोग के अनुरूप लगता है।

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