एक धारा में दो जावा 8 धाराएँ, या एक अतिरिक्त तत्व जोड़ना


168

मैं इस तरह धाराएँ या अतिरिक्त तत्व जोड़ सकता हूँ:

Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));

और मैं इस तरह से नया सामान जोड़ सकता हूं, जैसे:

Stream stream = Stream.concat(
                       Stream.concat(
                              stream1.filter(x -> x!=0), stream2)
                              .filter(x -> x!=1),
                                  Stream.of(element))
                                  .filter(x -> x!=2);

लेकिन यह बदसूरत है, क्योंकि concatस्थिर है। अगरconcat एक उदाहरण विधि थी, तो उपरोक्त उदाहरणों को पढ़ना बहुत आसान होगा:

 Stream stream = stream1.concat(stream2).concat(element);

तथा

 Stream stream = stream1
                 .filter(x -> x!=0)
                 .concat(stream2)
                 .filter(x -> x!=1)
                 .concat(element)
                 .filter(x -> x!=2);

मेरा सवाल यह है कि:

1) क्या कोई अच्छा कारण है concat है जो स्थैतिक है? या वहाँ कुछ समान उदाहरण विधि मुझे याद आ रही है?

2) किसी भी मामले में, क्या ऐसा करने का एक बेहतर तरीका है?


4
ऐसा लग रहा है कि बात हमेशा से ऐसी नहीं थी , लेकिन मैं सिर्फ इसका कारण नहीं खोज सकता।
एडविन डेलोरजो

जवाबों:


126

यदि आप Stream.concat और Stream.of के लिए स्थैतिक आयात जोड़ते हैं , तो पहला उदाहरण निम्नानुसार लिखा जा सकता है:

Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));

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

public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
    return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
    return Stream.concat(lhs, Stream.of(rhs));
}

इन दो स्थिर तरीकों (वैकल्पिक रूप से स्थिर आयात के साथ संयोजन में) के साथ, दो उदाहरण इस प्रकार लिखे जा सकते हैं:

Stream<Foo> stream = concat(stream1, concat(stream2, element));

Stream<Foo> stream = concat(
                         concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
                         element)
                     .filter(x -> x!=2);

कोड अब काफी छोटा है। हालाँकि, मैं मानता हूँ कि पठनीयता में सुधार नहीं हुआ है। तो मेरे पास एक और उपाय है।


बहुत सारी स्थितियों में, कलेक्टरों का उपयोग धाराओं की कार्यक्षमता को बढ़ाने के लिए किया जा सकता है। नीचे दो कलेक्टरों के साथ , दो उदाहरण इस प्रकार लिखे जा सकते हैं:

Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));

Stream<Foo> stream = stream1
                     .filter(x -> x!=0)
                     .collect(concat(stream2))
                     .filter(x -> x!=1)
                     .collect(concat(element))
                     .filter(x -> x!=2);

आपके इच्छित सिंटैक्स और ऊपर के सिंटैक्स के बीच एकमात्र अंतर यह है कि आपको कॉनकट (...) को कलेक्ट (कॉनकट ) (...) से बदलना होगा । दो स्थिर तरीकों को निम्नानुसार लागू किया जा सकता है (वैकल्पिक रूप से स्थैतिक आयात के साथ संयोजन में उपयोग किया जाता है):

private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
    return Collector.of(
        collector.supplier(),
        collector.accumulator(),
        collector.combiner(),
        collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
    return combine(Collectors.toList(),
        list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
    return concat(Stream.of(element));
}

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


1
मुझे concatकलेक्टर बहुत पठनीय नहीं लगते। यह इस तरह नामित एकल पैरामीटर स्थैतिक विधि के लिए अजीब लगता है, और यह भी उपयोग के collectलिए उपयोग किया जाता है।
दिदिर एल

@ एनोसिड, शायद इस थ्रेड के लिए थोड़ा सा ऑर्थोगोनल प्रश्न है लेकिन आप दावा क्यों करते हैं It's a bad idea to import static methods with names? मैं वास्तव में दिलचस्पी रखता हूं - मुझे लगता है कि यह कोड को अधिक संक्षिप्त और पठनीय बनाता है और बहुत सारे लोगों से मैंने भी यही सोचा था। कुछ उदाहरण प्रदान करने के लिए परवाह है कि आम तौर पर बुरा क्यों है?
क्वांटम

1
@ क्वेंटम: इसका क्या अर्थ है compare(reverse(getType(42)), of(6 * 9).hashCode())? ध्यान दें कि मैंने यह नहीं कहा कि स्थैतिक आयात एक बुरा विचार है, लेकिन जेनेरिक नामों जैसे और हैं के लिए स्थैतिक आयातofconcat
nosid

1
@ एनोसिड: आधुनिक आईडीई में प्रत्येक प्रतिमा पर मंडराते हुए जल्दी से अर्थ प्रकट नहीं करेंगे? किसी भी दर पर, मुझे लगता है कि यह यकीनन व्यक्तिगत प्राथमिकता वाला बयान हो सकता है, क्योंकि मुझे अभी भी कोई तकनीकी कारण नहीं दिखता है कि "जेनेरिक" नामों का स्थैतिक आयात बुरा क्यों है - जब तक कि आप किस स्थिति में प्रोग्रामिंग के लिए नोटपैड या VI (M) का उपयोग नहीं कर रहे हैं आपको बड़ी समस्याएं हैं।
क्वांटम

मैं यह कहने वाला नहीं हूं कि स्काला एसडीके बेहतर है, लेकिन ... उफ़ मैंने कहा।
इरिरलार

165

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

शुरुआत में Stream.concat (स्ट्रीम) के लिए एक इंस्टेंस विधि थी

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

इसमें वे उन मुद्दों पर चर्चा करते हैं जो उन मामलों से उत्पन्न हो सकते हैं जिनमें धारा अनंत हो सकती है और उन मामलों में क्या अर्थ होगा, लेकिन मुझे नहीं लगता कि यह संशोधन का कारण था।

आप इस अन्य थ्रेड में देखते हैं कि JDK 8 के कुछ शुरुआती उपयोगकर्ताओं ने अशक्त तर्कों के साथ उपयोग किए जाने पर कॉन्सैट इंस्टेंस विधि के व्यवहार के बारे में सवाल किया था।

हालांकि, यह एक अन्य सूत्र से पता चलता है, कि कॉनकैट विधि के डिजाइन पर चर्चा चल रही थी।

Streams.concat (धारा, स्ट्रीम) के लिए फिर से सक्रिय

लेकिन बिना किसी स्पष्टीकरण के, अचानक, विधियों को स्थिर तरीकों में बदल दिया गया, जैसा कि आप इस धागे में धाराओं के संयोजन के बारे में देख सकते हैं । यह शायद एकमात्र मेल थ्रेड है जो इस बदलाव के बारे में थोड़ा प्रकाश डालता है, लेकिन यह मेरे लिए पर्याप्त नहीं था कि रिफ्लेक्टरिंग का कारण निर्धारित किया जाए। लेकिन हम देख सकते हैं कि उन्होंने एक ऐसा कमिटमेंट किया जिसमें उन्होंने हेल्पर क्लास से concatबाहर जाने का तरीका सुझाया ।StreamStreams

Stream.concat (स्ट्रीम, स्ट्रीम) के लिए संदर्भित

बाद में इसे फिर से स्थानांतरित कर दिया StreamsगयाStream है, लेकिन एक बार फिर, उस के लिए कोई स्पष्टीकरण नहीं।

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

स्ट्रीम कॉनटेनटेशन के लिए कुछ विकल्प

माइकल हिक्सन द्वारा इस अन्य सूत्र पर चर्चा / संयोजन धाराओं के अन्य तरीकों के बारे में चर्चा की गई है

  1. दो धाराओं को मिलाने के लिए, मुझे यह करना चाहिए:

    Stream.concat(s1, s2)

    यह नहीं:

    Stream.of(s1, s2).flatMap(x -> x)

    ... सही?

  2. दो से अधिक धाराओं को संयोजित करने के लिए, मुझे यह करना चाहिए:

    Stream.of(s1, s2, s3, ...).flatMap(x -> x)

    यह नहीं:

    Stream.of(s1, s2, s3, ...).reduce(Stream.empty(), Stream::concat)

    ... सही?


6
+1 अच्छा शोध। और मैं अपने Stream.concat लेने के रूप में इस का उपयोग किया जाएगा varargs:public static <T> Stream<T> concat(Stream<T>... streams) { return Stream.of(streams).reduce(Stream.empty(), Stream::concat);}
MarcG

1
आज मैंने अपना स्वयं का संक्षिप्त संस्करण लिखा, और उसके बाद ही मैंने इस विषय को निधि दी। हस्ताक्षर थोड़ा अलग है, लेकिन इसके अधिक सामान्य धन्यवाद;) उदाहरण के लिए आप स्ट्रीम <Integer> और स्ट्रीम <Double> को स्ट्रीम <नंबर> में मर्ज कर सकते हैं। @SafeVarargs private static <T> Stream<T> concat(Stream<? extends T>... streams) { return Stream.of(streams).reduce(Stream.empty(),Stream::concat).map(Function.identity());}
कांट

@kant आपको Function.identity()मानचित्र की आवश्यकता क्यों है ? आखिरकार, यह वही तर्क देता है जो इसे प्राप्त होता है। परिणामी धारा में इसका कोई प्रभाव नहीं होना चाहिए। क्या मैं कुछ भूल रहा हूँ?
एडविन डेलोरजो

1
क्या आपने इसे अपने IDE में टाइप करने की कोशिश की है? .Map (पहचान) के बिना आपको संकलन त्रुटि मिलेगी। मैं स्ट्रीम <T> लौटना चाहता हूं लेकिन स्टेटमेंट: return Stream.of(streams).reduce(Stream.empty(),Stream::concat)रिटर्न स्ट्रीम <? फैली हुई टी>। (कुछ हद तक <टी> कुछ का उपप्रकार है <; टी का विस्तार?> नहीं, दूसरे तरीके से, इसलिए इसे कास्ट नहीं किया जा सकता) अतिरिक्त .map(identity())कास्ट <? T> को <T> तक बढ़ाता है। यह विधि तर्क और रिटर्न प्रकार और मानचित्र () विधि के हस्ताक्षर के जावा 8 'लक्ष्य प्रकार' के मिश्रण के लिए धन्यवाद होता है। वास्तव में यह फ़ंक्शन है। <T> पहचान ()।
कांत

1
@ कांट मुझे ऐसा करने में ज्यादा दम नजर नहीं आता ? extends T, क्योंकि आप कैप्चर कन्वर्जन का इस्तेमाल कर सकते हैं । किसी भी दर पर, यहां मेरा जिस्ट कोड स्निपेट है आइए हम जिस्ट में चर्चा जारी रखें।
एडविन डेलोरजो

12

मेरी स्ट्रीमटेक्स लाइब्रेरी स्ट्रीम एपीआई की कार्यक्षमता का विस्तार करती है। विशेष रूप से यह एपेंड और जैसे तरीकों की पेशकश करता है प्रीपेंड जो इस मुद्दे को हल करते हैं (आंतरिक रूप से वे उपयोग करते हैं concat)। ये विधियाँ या तो किसी अन्य स्ट्रीम या संग्रह या varargs सरणी को स्वीकार कर सकती हैं। मेरी लाइब्रेरी का उपयोग करके आपकी समस्या को इस तरह से हल किया जा सकता है (ध्यान दें कि x != 0गैर-आदिम धारा के लिए अजीब लग रहा है):

Stream<Integer> stream = StreamEx.of(stream1)
             .filter(x -> !x.equals(0))
             .append(stream2)
             .filter(x -> !x.equals(1))
             .append(element)
             .filter(x -> !x.equals(2));

वैसे आपके filterऑपरेशन के लिए एक शॉर्टकट भी है :

Stream<Integer> stream = StreamEx.of(stream1).without(0)
                                 .append(stream2).without(1)
                                 .append(element).without(2);

9

बस करो:

Stream.of(stream1, stream2, Stream.of(element)).flatMap(identity());

identity()का स्थैतिक आयात कहां हैFunction.identity()

एक धारा में कई धाराओं को समाप्‍त करना एक धारा को समतल करने के समान है।

हालांकि, दुर्भाग्य से, किसी कारण से कोई flatten()विधि नहीं है Stream, इसलिए आपको flatMap()पहचान फ़ंक्शन के साथ उपयोग करना होगा।



1

अगर आपको 3rd पार्टी लाइब्रेरीज़ साइक्लॉप्स-रिएक्शन का उपयोग करने में कोई आपत्ति नहीं है में एक विस्तारित स्ट्रीम प्रकार होता है, जो आपको एपेंड / प्रीपेंड ऑपरेटरों के माध्यम से ऐसा करने की अनुमति देगा।

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

Stream stream = ReactiveSeq.of(1,2)
                           .filter(x -> x!=0)
                           .append(ReactiveSeq.of(3,4))
                           .filter(x -> x!=1)
                           .append(5)
                           .filter(x -> x!=2);

[प्रकटीकरण मैं चक्रवात-प्रतिक्रिया का प्रमुख विकासकर्ता हूं]


1

दिन के अंत में मुझे धाराओं के संयोजन में कोई दिलचस्पी नहीं है, लेकिन उन सभी धाराओं के प्रत्येक तत्व को संसाधित करने के संयुक्त परिणाम प्राप्त करने में।

जबकि संयोजन धाराओं बोझिल साबित हो सकती हैं (इस प्रकार यह धागा), उनके प्रसंस्करण के परिणामों का संयोजन काफी आसान है।

हल करने की कुंजी यह है कि आप अपना खुद का कलेक्टर बनाएं और यह सुनिश्चित करें कि नए कलेक्टर के लिए आपूर्तिकर्ता फ़ंक्शन हर बार एक ही संग्रह लौटाता है ( नया नहीं ), नीचे दिया गया कोड इस दृष्टिकोण को दिखाता है।

package scratchpad;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Stream;

public class CombineStreams {
    public CombineStreams() {
        super();
    }

    public static void main(String[] args) {
        List<String> resultList = new ArrayList<>();
        Collector<String, List<String>, List<String>> collector = Collector.of(
                () -> resultList,
                (list, item) -> {
                    list.add(item);
                },
                (llist, rlist) -> {
                    llist.addAll(rlist);
                    return llist;
                }
        );
        String searchString = "Wil";

        System.out.println("After processing first stream\n"
                + createFirstStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

        System.out.println("After processing second stream\n"
                + createSecondStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

        System.out.println("After processing third stream\n"
                + createThirdStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

    }

    private static Stream<String> createFirstStream() {
        return Arrays.asList(
                "William Shakespeare",
                "Emily Dickinson",
                "H. P. Lovecraft",
                "Arthur Conan Doyle",
                "Leo Tolstoy",
                "Edgar Allan Poe",
                "Robert Ervin Howard",
                "Rabindranath Tagore",
                "Rudyard Kipling",
                "Seneca",
                "John Donne",
                "Sarah Williams",
                "Oscar Wilde",
                "Catullus",
                "Alfred Tennyson",
                "William Blake",
                "Charles Dickens",
                "John Keats",
                "Theodor Herzl"
        ).stream();
    }

    private static Stream<String> createSecondStream() {
        return Arrays.asList(
                "Percy Bysshe Shelley",
                "Ernest Hemingway",
                "Barack Obama",
                "Anton Chekhov",
                "Henry Wadsworth Longfellow",
                "Arthur Schopenhauer",
                "Jacob De Haas",
                "George Gordon Byron",
                "Jack London",
                "Robert Frost",
                "Abraham Lincoln",
                "O. Henry",
                "Ovid",
                "Robert Louis Stevenson",
                "John Masefield",
                "James Joyce",
                "Clark Ashton Smith",
                "Aristotle",
                "William Wordsworth",
                "Jane Austen"
        ).stream();
    }

    private static Stream<String> createThirdStream() {
        return Arrays.asList(
                "Niccolò Machiavelli",
                "Lewis Carroll",
                "Robert Burns",
                "Edgar Rice Burroughs",
                "Plato",
                "John Milton",
                "Ralph Waldo Emerson",
                "Margaret Thatcher",
                "Sylvie d'Avigdor",
                "Marcus Tullius Cicero",
                "Banjo Paterson",
                "Woodrow Wilson",
                "Walt Whitman",
                "Theodore Roosevelt",
                "Agatha Christie",
                "Ambrose Bierce",
                "Nikola Tesla",
                "Franz Kafka"
        ).stream();
    }
}

0

कैसे अपनी खुद की कॉन्कैट विधि लिखने के बारे में?

public static Stream<T> concat(Stream<? extends T> a, 
                               Stream<? extends T> b, 
                               Stream<? extends T> args)
{
    Stream<T> concatenated = Stream.concat(a, b);
    for (Stream<T> stream : args)
    {
        concatenated = Stream.concat(concatenated, stream);
    }
    return concatenated;
}

यह कम से कम आपके पहले उदाहरण को बहुत अधिक पठनीय बनाता है।


1
बार-बार संघनन से धाराओं का निर्माण करते समय सावधानी बरतें। एक गहराई से प्रवाहित धारा के एक तत्व तक पहुँचने के परिणामस्वरूप गहरी कॉल श्रृंखला या यहां तक ​​कि StackOverflowError हो सकती है।
लीगना
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.