खोज…


परिचय

Stream तत्वों के एक अनुक्रम का प्रतिनिधित्व करता है और उन तत्वों पर गणना करने के लिए विभिन्न प्रकार के संचालन का समर्थन करता है। जावा 8 के साथ, Collection इंटरफ़ेस में Stream उत्पन्न करने के लिए दो विधियाँ हैं: stream() और parallelStream()Stream संचालन या तो मध्यवर्ती या टर्मिनल हैं। इंटरमीडिएट ऑपरेशन एक Stream लौटाता है इसलिए Stream के बंद होने से पहले कई मध्यवर्ती संचालन को जंजीर में डाला जा सकता है। टर्मिनल ऑपरेशन या तो शून्य हैं या एक गैर-स्ट्रीम परिणाम लौटाते हैं।

वाक्य - विन्यास

  • collection.stream ()
  • Arrays.stream (सरणी)
  • स्ट्रीम.इंटरेट (फ़र्स्टवैल्यू, करंटवैलू -> नेक्स्ट वेल्यू)
  • Stream.generate () -> मान)
  • Stream.of (elementOfT [, elementOfT, ...])
  • Stream.empty ()
  • StreamSupport.stream (iterable.spliterator (), झूठा)

धाराओं का उपयोग करना

Stream उन तत्वों का एक अनुक्रम है, जिन पर अनुक्रमिक और समानांतर कुल संचालन किया जा सकता है। किसी भी दी गई Stream संभवतः असीमित मात्रा में डेटा बह सकता है। परिणामस्वरूप, Stream से प्राप्त डेटा को व्यक्तिगत रूप से संसाधित किया जाता है क्योंकि यह पूरी तरह से डेटा पर बैच प्रोसेसिंग करने का विरोध करता है। जब लंबोदर भावों के साथ संयुक्त होते हैं तो वे एक कार्यात्मक दृष्टिकोण का उपयोग करके डेटा के अनुक्रमों पर संचालन करने के लिए एक संक्षिप्त तरीका प्रदान करते हैं।

उदाहरण: ( इसे आइडोन पर काम देखें )

Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");

fruitStream.filter(s -> s.contains("a"))
           .map(String::toUpperCase)
           .sorted()
           .forEach(System.out::println);

आउटपुट:

सेब
केला
संतरा
नाशपाती

उपरोक्त कोड द्वारा किए गए कार्यों को संक्षेप में प्रस्तुत किया जा सकता है:

  1. एक Stream<String> बनाएँ Stream<String> स्थिर फैक्ट्री विधि Stream.of(values) का उपयोग करके क्रमबद्ध ऑर्डर किए गए फलों के String युक्त Stream

  2. filter() ऑपरेशन केवल उन्हीं तत्वों को बनाए रखता है जो किसी दिए गए विधेय से मेल खाते हैं (वे तत्व जो कि विधेय द्वारा जांचे जाने पर वापस लौटते हैं)। इस मामले में, यह "a" वाले तत्वों को बरकरार रखता है। विधेय को लंबोदर अभिव्यक्ति के रूप में दिया जाता है।

  3. map() ऑपरेशन दिए गए फ़ंक्शन का उपयोग करके प्रत्येक तत्व को बदल देता है, जिसे मैपर कहा जाता है। इस स्थिति में, प्रत्येक फल String को विधि-संदर्भ String::toUppercase का उपयोग करके इसके अपरकेस String संस्करण में मैप किया जाता है।

    ध्यान दें कि यदि मैपिंग फ़ंक्शन अपने इनपुट पैरामीटर के लिए एक अलग प्रकार देता है तो map() ऑपरेशन एक अलग जेनेरिक प्रकार के साथ एक धारा लौटाएगा। उदाहरण के लिए Stream<String> कॉलिंग .map(String::isEmpty) Stream<Boolean>

  4. sorted() ऑपरेशन Stream के तत्वों को उनकी प्राकृतिक ऑर्डरिंग के अनुसार क्रमबद्ध करता है (शाब्दिक रूप से String के मामले में)।

  5. अंत में, forEach(action) ऑपरेशन एक एक्शन करता है जो Stream प्रत्येक तत्व पर कार्य करता है, इसे एक उपभोक्ता को दे रहा है । उदाहरण में, प्रत्येक तत्व को केवल कंसोल पर प्रिंट किया जा रहा है। यह ऑपरेशन एक टर्मिनल ऑपरेशन है, इस प्रकार इसे फिर से संचालित करना असंभव है।

    ध्यान दें कि Stream पर परिभाषित ऑपरेशन टर्मिनल ऑपरेशन के कारण किए जाते हैं। एक टर्मिनल ऑपरेशन के बिना, धारा संसाधित नहीं होती है। धाराओं का पुन: उपयोग नहीं किया जा सकता है। एक बार टर्मिनल ऑपरेशन कहा जाता है, Stream ऑब्जेक्ट अनुपयोगी हो जाता है।

जंजीर संचालन

संचालन (जैसा कि ऊपर देखा गया है) डेटा को एक क्वेरी के रूप में देखा जा सकता है जो बनाने के लिए एक साथ जंजीर है।


समापन धाराएँ

ध्यान दें कि आम तौर पर Stream को बंद नहीं करना पड़ता है। आईओ चैनलों पर काम करने वाली धाराओं को बंद करना आवश्यक है। अधिकांश Stream प्रकार संसाधनों पर काम नहीं करते हैं और इसलिए उन्हें बंद करने की आवश्यकता नहीं है।

Stream इंटरफ़ेस AutoCloseable विस्तार करता है। close विधि को कॉल करके या कोशिश-के साथ-साथ संसाधनों का उपयोग करके धाराओं को बंद किया जा सकता है।

एक उदाहरण उपयोग का मामला जहां Stream को बंद किया जाना चाहिए, जब आप किसी फ़ाइल से लाइनों की Stream बनाते हैं:

try (Stream<String> lines = Files.lines(Paths.get("somePath"))) {
    lines.forEach(System.out::println);
}

Stream इंटरफ़ेस भी वाणी Stream.onClose() विधि है जो आप रजिस्टर करने के लिए अनुमति देता है Runnable संचालकों जो जब धारा बंद कर दिया है बुलाया जाएगा। एक उदाहरण उपयोग मामला वह कोड है जो एक धारा उत्पन्न करता है, यह जानने की जरूरत है कि कब कुछ सफाई करने के लिए इसका सेवन किया जाता है।

public Stream<String>streamAndDelete(Path path) throws IOException {
    return Files.lines(path).onClose(() -> someClass.deletePath(path));
}

रन हैंडलर केवल तभी निष्पादित होगा यदि close() विधि को कॉल किया जाता है, या तो स्पष्ट रूप से या अंतर्निहित रूप से कोशिश-के-साथ-साथ स्टेटमेंट।


प्रसंस्करण आदेश

Stream ऑब्जेक्ट की प्रोसेसिंग अनुक्रमिक या समानांतर हो सकती है।

अनुक्रमिक मोड में, तत्वों को Stream के स्रोत के क्रम में संसाधित किया जाता है। यदि Stream का आदेश दिया गया है (जैसे कि एक SortedMap कार्यान्वयन या एक List ) तो स्रोत के आदेश से मेल खाने के लिए प्रसंस्करण की गारंटी है। अन्य मामलों में, हालांकि, देखभाल आदेश पर निर्भर नहीं लिया जाना चाहिए (देखें: जावा है HashMap keySet() ? पुनरावृत्ति संगत आदेश )।

उदाहरण:

List<Integer> integerList = Arrays.asList(0, 1, 2, 3, 42); 

// sequential 
long howManyOddNumbers = integerList.stream()
                                    .filter(e -> (e % 2) == 1)
                                    .count(); 

System.out.println(howManyOddNumbers); // Output: 2

Ideone पर रहते हैं

समानांतर मोड कई कोर पर कई थ्रेड्स के उपयोग की अनुमति देता है, लेकिन इसमें उस आदेश की कोई गारंटी नहीं है जिसमें तत्वों को संसाधित किया जाता है।

यदि कई विधियों को अनुक्रमिक Stream पर बुलाया जाता है, तो हर विधि को लागू नहीं करना पड़ता है। उदाहरण के लिए, अगर एक Stream फ़िल्टर और है तत्वों की संख्या एक, इस तरह के रूप में एक विधि के लिए बाद में एक कॉल करने के लिए कम हो जाता है sort घटित नहीं होगा। यह एक अनुक्रमिक Stream के प्रदर्शन को बढ़ा सकता है - एक अनुकूलन जो समानांतर Stream साथ संभव नहीं है।

उदाहरण:

// parallel
long howManyOddNumbersParallel = integerList.parallelStream()
                                            .filter(e -> (e % 2) == 1)
                                            .count();

System.out.println(howManyOddNumbersParallel); // Output: 2

Ideone पर रहते हैं


कंटेनरों से अंतर (या संग्रह )

जबकि कंटेनरों और धाराओं दोनों पर कुछ क्रियाएं की जा सकती हैं, वे अंततः विभिन्न उद्देश्यों की पूर्ति करते हैं और विभिन्न कार्यों का समर्थन करते हैं। कंटेनर अधिक ध्यान केंद्रित करते हैं कि तत्वों को कैसे संग्रहीत किया जाता है और उन तत्वों को कैसे कुशलता से एक्सेस किया जा सकता है। दूसरी ओर, एक Stream , इसके तत्वों तक सीधी पहुंच और हेरफेर प्रदान नहीं करती है; यह एक सामूहिक इकाई के रूप में वस्तुओं के समूह को समर्पित है और समग्र रूप से उस इकाई पर कार्य कर रहा है। इन अलग-अलग उद्देश्यों के लिए Stream और Collection अलग-अलग उच्च-स्तरीय सार हैं।

एक स्ट्रीम में एक स्ट्रीम के तत्वों को ले लीजिए

कलेक्ट toList() और toSet()

Stream.collect ऑपरेशन का उपयोग करके Stream से तत्वों को कंटेनर में आसानी से एकत्र किया जा सकता है:

System.out.println(Arrays
    .asList("apple", "banana", "pear", "kiwi", "orange")
    .stream()
    .filter(s -> s.contains("a"))
    .collect(Collectors.toList())
);
// prints: [apple, banana, pear, orange]

अन्य संग्रह उदाहरण, जैसे कि एक Set , अन्य Collectors द्वारा निर्मित तरीकों का उपयोग करके बनाया जा सकता है। उदाहरण के लिए, Collectors.toSet() toSet Collectors.toSet() Stream के तत्वों को एक Set में एकत्रित करता है।


List या Set के कार्यान्वयन पर स्पष्ट नियंत्रण

Collectors#toList() और Collectors#toSet() प्रलेखन के अनुसार, List या Set के प्रकार, परिवर्तनशीलता, क्रमबद्धता, या थ्रेड-सुरक्षा पर कोई गारंटी नहीं है।

लौटाए जाने वाले कार्यान्वयन पर स्पष्ट नियंत्रण के लिए, Collectors#toCollection(Supplier) का उपयोग किया जा सकता है, जहां दिए गए आपूर्तिकर्ता एक नया और खाली संग्रह लौटाता है।

// syntax with method reference
System.out.println(strings
        .stream()
        .filter(s -> s != null && s.length() <= 3)
        .collect(Collectors.toCollection(ArrayList::new))
);

// syntax with lambda
System.out.println(strings
        .stream()
        .filter(s -> s != null && s.length() <= 3)
        .collect(Collectors.toCollection(() -> new LinkedHashSet<>()))
);

तत्वों का उपयोग कर एकत्रित करना

कलेक्टर तत्वों को एक मानचित्र में संचित करता है, जहां कुंजी छात्र आईडी है और मूल्य छात्र मूल्य है।

  List<Student> students = new ArrayList<Student>(); 
    students.add(new Student(1,"test1"));
    students.add(new Student(2,"test2"));
    students.add(new Student(3,"test3"));
    
    Map<Integer, String> IdToName = students.stream()
        .collect(Collectors.toMap(Student::getId, Student::getName));
    System.out.println(IdToName);

आउटपुट:

{1=test1, 2=test2, 3=test3}

संग्राहक. Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) का एक अन्य कार्यान्वयन Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) । मर्जफंक्शन का उपयोग या तो नए मूल्य का चयन करने के लिए किया जाता है या पुराने मूल्य को बनाए रखने के लिए किया जाता है, यदि सूची में एक नए सदस्य को मानचित्र से जोड़ते समय दोहराया जाता है।

मर्जफ़ंक्शन अक्सर ऐसा दिखता है: (s1, s2) -> s1 दोहराया कुंजी के लिए इसी मूल्य को बनाए रखने के लिए, या (s1, s2) -> s2 दोहराया कुंजी के लिए नए मूल्य डालने के लिए।

संग्रह के तत्वों को एकत्रित करना

उदाहरण: ArrayList से लेकर Map <String, List <>>

अक्सर इसे प्राथमिक सूची से बाहर सूची का नक्शा बनाने की आवश्यकता होती है। उदाहरण: सूची के छात्र से, हमें प्रत्येक छात्र के लिए विषयों की सूची का नक्शा बनाने की आवश्यकता है।

    List<Student> list = new ArrayList<>();
    list.add(new Student("Davis", SUBJECT.MATH, 35.0));
    list.add(new Student("Davis", SUBJECT.SCIENCE, 12.9));
    list.add(new Student("Davis", SUBJECT.GEOGRAPHY, 37.0));

    list.add(new Student("Sascha", SUBJECT.ENGLISH, 85.0));
    list.add(new Student("Sascha", SUBJECT.MATH, 80.0));
    list.add(new Student("Sascha", SUBJECT.SCIENCE, 12.0));
    list.add(new Student("Sascha", SUBJECT.LITERATURE, 50.0));

    list.add(new Student("Robert", SUBJECT.LITERATURE, 12.0));

    Map<String, List<SUBJECT>> map = new HashMap<>();
    list.stream().forEach(s -> {
                map.computeIfAbsent(s.getName(), x -> new ArrayList<>()).add(s.getSubject());
            });
    System.out.println(map);

आउटपुट:

{ Robert=[LITERATURE], 
Sascha=[ENGLISH, MATH, SCIENCE, LITERATURE], 
Davis=[MATH, SCIENCE, GEOGRAPHY] }

उदाहरण: ArrayList से Map <String, Map <>> के लिए

    List<Student> list = new ArrayList<>();
    list.add(new Student("Davis", SUBJECT.MATH, 1, 35.0));
    list.add(new Student("Davis", SUBJECT.SCIENCE, 2, 12.9));
    list.add(new Student("Davis", SUBJECT.MATH, 3, 37.0));
    list.add(new Student("Davis", SUBJECT.SCIENCE, 4, 37.0));

    list.add(new Student("Sascha", SUBJECT.ENGLISH, 5, 85.0));
    list.add(new Student("Sascha", SUBJECT.MATH, 1, 80.0));
    list.add(new Student("Sascha", SUBJECT.ENGLISH, 6, 12.0));
    list.add(new Student("Sascha", SUBJECT.MATH, 3, 50.0));

    list.add(new Student("Robert", SUBJECT.ENGLISH, 5, 12.0));

    Map<String, Map<SUBJECT, List<Double>>> map = new HashMap<>();

    list.stream().forEach(student -> {
        map.computeIfAbsent(student.getName(), s -> new HashMap<>())
                .computeIfAbsent(student.getSubject(), s -> new ArrayList<>())
                .add(student.getMarks());
    });

    System.out.println(map);

आउटपुट:

{ Robert={ENGLISH=[12.0]}, 
Sascha={MATH=[80.0, 50.0], ENGLISH=[85.0, 12.0]}, 
Davis={MATH=[35.0, 37.0], SCIENCE=[12.9, 37.0]} }

प्रवंचक पत्रक

लक्ष्य कोड
एक List लिए लीजिए Collectors.toList()
पूर्व-आवंटित आकार के साथ एक ArrayList जमा करें Collectors.toCollection(() -> new ArrayList<>(size))
एक Set लीजिए Collectors.toSet()
बेहतर पुनरावृत्ति प्रदर्शन के साथ एक Set Collectors.toCollection(() -> new LinkedHashSet<>())
केस-असंवेदनशील Set<String> Collectors.toCollection(() -> new TreeSet<>(String.CASE_INSENSITIVE_ORDER))
एक EnumSet<AnEnum> (enums के लिए सर्वश्रेष्ठ प्रदर्शन) Collectors.toCollection(() -> EnumSet.noneOf(AnEnum.class))
अद्वितीय कुंजियों के साथ एक Map<K,V> Collectors.toMap(keyFunc,valFunc)
मानचित्र MyObject.getter () अद्वितीय MyObject के लिए Collectors.toMap(MyObject::getter, Function.identity())
कई MyObjects के लिए MyObject.getter () मैप करें Collectors.groupingBy(MyObject::getter)

अनंत धाराएँ

एक Stream उत्पन्न करना संभव है जो समाप्त नहीं होता है। एक अनंत Stream पर एक टर्मिनल विधि को कॉल करने से Stream एक अनंत लूप में प्रवेश करती है। Stream की limit विधि का उपयोग जावा प्रक्रियाओं की Stream की संख्या को सीमित करने के लिए किया जा सकता है।

यह उदाहरण एक उत्पन्न करता Stream , सभी प्राकृतिक संख्या की संख्या 1. के साथ शुरू की प्रत्येक लगातार अवधि Stream पिछले एक से अधिक है। इस की सीमा विधि को फोन करके Stream , केवल के पहले पांच मामले Stream माना जाता है और मुद्रित कर रहे हैं।

// Generate infinite stream - 1, 2, 3, 4, 5, 6, 7, ...
IntStream naturalNumbers = IntStream.iterate(1, x -> x + 1);

// Print out only the first 5 terms
naturalNumbers.limit(5).forEach(System.out::println);

आउटपुट:

1
2
3
4
5


एक अनंत धारा उत्पन्न करने का दूसरा तरीका Stream.generate पद्धति का उपयोग करना है। यह विधि प्रकार आपूर्तिकर्ता का एक लंबोदर लेती है।

// Generate an infinite stream of random numbers
Stream<Double> infiniteRandomNumbers = Stream.generate(Math::random);

// Print out only the first 10 random numbers
infiniteRandomNumbers.limit(10).forEach(System.out::println);

उपभोग की धाराएँ

जब कोई टर्मिनल ऑपरेशन होता है तो एक Stream पता लगाया जाएगा, जैसे कि count() , collect() या forEach() । अन्यथा, Stream पर कोई भी ऑपरेशन नहीं किया जाएगा।

निम्नलिखित उदाहरण में, Stream कोई टर्मिनल ऑपरेशन नहीं जोड़ा गया है, इसलिए filter() ऑपरेशन को लागू नहीं किया जाएगा और कोई आउटपुट नहीं उत्पन्न होगा क्योंकि peek() एक टर्मिनल ऑपरेशन नहीं है।

IntStream.range(1, 10).filter(a -> a % 2 == 0).peek(System.out::println);

Ideone पर रहते हैं

यह एक वैध टर्मिनल ऑपरेशन के साथ Stream अनुक्रम है, इस प्रकार एक आउटपुट का उत्पादन होता है।

तुम भी इस्तेमाल कर सकते हैं forEach के बजाय peek :

IntStream.range(1, 10).filter(a -> a % 2 == 0).forEach(System.out::println); 

Ideone पर रहते हैं

आउटपुट:

2
4
6
8

टर्मिनल ऑपरेशन किए जाने के बाद, Stream का उपभोग किया जाता है और पुन: उपयोग नहीं किया जा सकता है।


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

List<String> list = Arrays.asList("FOO", "BAR");
Iterable<String> iterable = () -> list.stream().map(String::toLowerCase).iterator();

for (String str : iterable) {
    System.out.println(str);
}
for (String str : iterable) {
    System.out.println(str);
}

आउटपुट:

foo
बार
foo
बार

यह काम करता है क्योंकि Iterable एक अमूर्त विधि Iterator<T> iterator() घोषित करता है। यह एक लैंबडा द्वारा कार्यान्वित प्रभावी रूप से एक कार्यात्मक इंटरफ़ेस बनाता है, जो प्रत्येक कॉल पर एक नई धारा बनाता है।


सामान्य तौर पर, Stream निम्न छवि में दिखाया गया है:

स्ट्रीम ऑपरेशन

नोट : तर्क जांच हमेशा टर्मिनल ऑपरेशन के बिना भी की जाती है:

try {
    IntStream.range(1, 10).filter(null);
} catch (NullPointerException e) {
    System.out.println("We got a NullPointerException as null was passed as an argument to filter()");
}

Ideone पर रहते हैं

आउटपुट:

हमें NullPointerException मिल गई क्योंकि नल को फ़िल्टर करने के तर्क के रूप में पारित किया गया था ()

फ़्रीक्वेंसी मैप बनाना

groupingBy(classifier, downstream) कलेक्टर एक समूह में प्रत्येक तत्व को वर्गीकृत करके और एक ही समूह में वर्गीकृत तत्वों पर डाउनस्ट्रीम ऑपरेशन करके Map तत्वों में Stream तत्वों के संग्रह की अनुमति देता है।

इस सिद्धांत का एक उत्कृष्ट उदाहरण एक Stream में तत्वों की घटनाओं को गिनने के लिए Map का उपयोग करना है। इस उदाहरण में, क्लासिफायरियर केवल पहचान फ़ंक्शन है, जो तत्व को वापस लौटाता है। डाउनस्ट्रीम ऑपरेशन counting() का उपयोग करके, समान तत्वों की संख्या को गिनता है।

Stream.of("apple", "orange", "banana", "apple")
      .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
      .entrySet()
      .forEach(System.out::println);

डाउनस्ट्रीम ऑपरेशन खुद एक कलेक्टर ( Collectors.counting() ) है जो टाइप स्ट्रिंग के तत्वों पर काम करता है और टाइप Long परिणामस्वरूप उत्पन्न होता है। collect मेथड कॉल का परिणाम Map<String, Long>

यह निम्न आउटपुट का उत्पादन करेगा:

केले = 1
नारंगी = 1
सेब = 2

समानांतर स्ट्रीम

नोट: तय करने से पहले कि Stream का उपयोग करने के लिए कृपया ParallelStream बनाम Sequential Stream व्यवहार पर एक नज़र डालें।

जब आप Stream ऑपरेशन को समवर्ती रूप से करना चाहते हैं, तो आप इनमें से किसी भी तरीके का उपयोग कर सकते हैं।

List<String> data = Arrays.asList("One", "Two", "Three", "Four", "Five");
Stream<String> aParallelStream = data.stream().parallel();

या:

Stream<String> aParallelStream = data.parallelStream();

समानांतर स्ट्रीम के लिए परिभाषित परिचालनों को निष्पादित करने के लिए, टर्मिनल ऑपरेटर को कॉल करें:

aParallelStream.forEach(System.out::println);

(संभावित) समानांतर Stream से आउटपुट:

तीन
चार
एक
दो
पांच

आदेश बदल सकता है क्योंकि सभी तत्व समानांतर में संसाधित होते हैं (जो इसे तेज कर सकता है )। जब ऑर्डर करने से कोई फर्क नहीं पड़ता तब parallelStream स्ट्र्रीम का उपयोग करें।

प्रदर्शन प्रभाव

यदि नेटवर्किंग शामिल है, तो समानांतर Stream एस किसी एप्लिकेशन के समग्र प्रदर्शन को नीचा दिखा सकता है क्योंकि सभी समानांतर Stream नेटवर्क के लिए एक सामान्य कांटा-जुड़ने वाले थ्रेड पूल का उपयोग करते हैं।

दूसरी ओर, समानांतर Stream एस वर्तमान में चल रहे सीपीयू में उपलब्ध कोर की संख्या के आधार पर कई अन्य मामलों में प्रदर्शन में काफी सुधार कर सकता है।

मानों की एक स्ट्रीम के लिए वैकल्पिक की एक धारा परिवर्तित

आप एक कन्वर्ट करने के लिए आवश्यकता हो सकती है Stream उत्सर्जक Optional एक को Stream मौजूदा से केवल मूल्यों उत्सर्जक, मूल्यों की Optional । (यानी: null मान के बिना और Optional.empty() ) के साथ काम नहीं कर रहा है।

 Optional<String> op1 = Optional.empty();
 Optional<String> op2 = Optional.of("Hello World");

 List<String> result = Stream.of(op1, op2)
                             .filter(Optional::isPresent)
                             .map(Optional::get)
                             .collect(Collectors.toList());

 System.out.println(result); //[Hello World]

एक स्ट्रीम बनाना

सभी जावा Collection<E> stream() और parallelStream() विधियाँ हैं जिनसे Stream<E> का निर्माण किया जा सकता है:

Collection<String> stringList = new ArrayList<>();
Stream<String> stringStream = stringList.parallelStream();

एक Stream<E> को दो तरीकों में से एक का उपयोग करके एक सरणी से बनाया जा सकता है:

String[] values = { "aaa", "bbbb", "ddd", "cccc" };
Stream<String> stringStream = Arrays.stream(values);
Stream<String> stringStreamAlternative = Stream.of(values);

Arrays.stream() और Stream.of() बीच का अंतर यह है कि Stream.of() में एक varargs पैरामीटर है, इसलिए इसका उपयोग इस तरह किया जा सकता है:

Stream<Integer> integerStream = Stream.of(1, 2, 3);

वहाँ भी आदिम Stream कि आप का उपयोग कर सकते हैं। उदाहरण के लिए:

IntStream intStream = IntStream.of(1, 2, 3);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);

इन आदिम धाराओं का निर्माण Arrays.stream() विधि का उपयोग करके भी किया जा सकता है:

IntStream intStream = Arrays.stream(new int[]{ 1, 2, 3 });

एक निर्दिष्ट श्रेणी के साथ एक सरणी से Stream बनाना संभव है।

int[] values= new int[]{1, 2, 3, 4, 5};
IntStream intStram = Arrays.stream(values, 1, 3);

ध्यान दें कि किसी भी आदिम धारा को boxed पद्धति का उपयोग करके बॉक्सिंग प्रकार की धारा में बदला जा सकता है:

Stream<Integer> integerStream = intStream.boxed();

यह किसी मामले में उपयोगी हो सकता है यदि आप डेटा एकत्र करना चाहते हैं क्योंकि आदिम धारा में कोई collect विधि नहीं है जो एक Collector को तर्क के रूप में लेती है।

एक धारा श्रृंखला के मध्यवर्ती संचालन का पुन: उपयोग करना

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

Supplier<Stream<String>> streamSupplier = () -> Stream.of("apple", "banana","orange", "grapes", "melon","blueberry","blackberry")
.map(String::toUpperCase).sorted();

  streamSupplier.get().filter(s ->   s.startsWith("A")).forEach(System.out::println);

// APPLE

  streamSupplier.get().filter(s -> s.startsWith("B")).forEach(System.out::println);

  // BANANA
  // BLACKBERRY
  // BLUEBERRY

int[] सरणियों को List<Integer> धाराओं का उपयोग करके परिवर्तित किया जा सकता है

int[] ints = {1,2,3};
List<Integer> list = IntStream.of(ints).boxed().collect(Collectors.toList());

न्यूमेरिकल स्ट्रीम के बारे में आंकड़े खोजना

जावा 8 IntSummaryStatistics , DoubleSummaryStatistics और LongSummaryStatistics नामक कक्षाएं प्रदान करता है, जो count , min , max , sum और average जैसे आंकड़े एकत्र करने के लिए एक राज्य वस्तु प्रदान करती हैं।

जावा एसई 8
List<Integer> naturalNumbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntSummaryStatistics stats = naturalNumbers.stream()
                                           .mapToInt((x) -> x)
                                           .summaryStatistics();
System.out.println(stats);

जिसके परिणामस्वरूप होगा:

जावा एसई 8
IntSummaryStatistics{count=10, sum=55, min=1, max=10, average=5.500000}

एक स्ट्रीम का स्लाइस प्राप्त करें

उदाहरण: एक संग्रह के 21 वें से 50 वें (समावेशी) तत्व वाले 30 तत्वों की एक Stream प्राप्त करें।

final long n = 20L; // the number of elements to skip
final long maxSize = 30L; // the number of elements the stream should be limited to
final Stream<T> slice = collection.stream().skip(n).limit(maxSize);

टिप्पणियाँ:

  • IllegalArgumentException अगर फेंक दिया जाता है n नकारात्मक है या maxSize नकारात्मक है
  • दोनों skip(long) और limit(long) मध्यवर्ती संचालन हैं
  • यदि किसी स्ट्रीम में n तत्व से कम है तो skip(n) एक खाली स्ट्रीम देता है
  • दोनों skip(long) और limit(long) अनुक्रमिक धारा पाइपलाइनों पर सस्ते संचालन हैं, लेकिन क्रमबद्ध समानांतर पाइपलाइनों पर काफी महंगा हो सकता है

कॉनटेटनेट स्ट्रीम

उदाहरण के लिए परिवर्तनीय घोषणा:

Collection<String> abc = Arrays.asList("a", "b", "c");
Collection<String> digits = Arrays.asList("1", "2", "3");
Collection<String> greekAbc = Arrays.asList("alpha", "beta", "gamma");

उदाहरण 1 - दो Stream एस को मिलाएं

final Stream<String> concat1 = Stream.concat(abc.stream(), digits.stream());

concat1.forEach(System.out::print);
// prints: abc123

उदाहरण 2 - दो से अधिक Stream एस को मिलाएं

final Stream<String> concat2 = Stream.concat(
    Stream.concat(abc.stream(), digits.stream()),
    greekAbc.stream());

System.out.println(concat2.collect(Collectors.joining(", ")));
// prints: a, b, c, 1, 2, 3, alpha, beta, gamma

नेस्टेड concat() को सरल बनाने के लिए वैकल्पिक रूप से concat() सिंटैक्स Stream साथ भी flatMap() जा सकता है flatMap() :

final Stream<String> concat3 = Stream.of(
    abc.stream(), digits.stream(), greekAbc.stream())
    .flatMap(s -> s);
    // or `.flatMap(Function.identity());` (java.util.function.Function)

System.out.println(concat3.collect(Collectors.joining(", ")));
// prints: a, b, c, 1, 2, 3, alpha, beta, gamma

बार-बार संघनन से Stream एस का निर्माण करते समय सावधानी बरतें, क्योंकि एक गहरी कंक्रीटिंग Stream के एक तत्व तक पहुँचने के परिणामस्वरूप गहरी कॉल श्रृंखला या यहां तक कि एक StackOverflowException

स्ट्रिंग के लिए Intream

जावा इसलिए जब साथ काम कर रहे एक चार स्ट्रीम नहीं है, String और एक निर्माण Stream का Character है, एक विकल्प एक पाने के लिए है IntStream का उपयोग कर कोड अंक की String.codePoints() विधि। तो IntStream को नीचे के रूप में प्राप्त किया जा सकता है:

public IntStream stringToIntStream(String in) {
  return in.codePoints();
}

यह रूपांतरण के दूसरे तरीके यानी IntStreamToString के आसपास करने के लिए थोड़ा अधिक शामिल है। जो निम्नानुसार किया जा सकता है:

public String intStreamToString(IntStream intStream) {
  return intStream.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
}

स्ट्रीम का उपयोग करके सॉर्ट करें

List<String> data = new ArrayList<>();
data.add("Sydney");
data.add("London");
data.add("New York");
data.add("Amsterdam");
data.add("Mumbai");
data.add("California");

System.out.println(data);

List<String> sortedData = data.stream().sorted().collect(Collectors.toList());

System.out.println(sortedData);

आउटपुट:

[Sydney, London, New York, Amsterdam, Mumbai, California]
[Amsterdam, California, London, Mumbai, New York, Sydney]

विभिन्न तुलना तंत्र का उपयोग करना भी संभव है क्योंकि एक अतिभारित sorted संस्करण है जो इसके तर्क के रूप में एक तुलनित्र लेता है।

इसके अलावा, आप छँटाई के लिए एक लैम्ब्डा अभिव्यक्ति का उपयोग कर सकते हैं:

List<String> sortedData2 = data.stream().sorted((s1,s2) -> s2.compareTo(s1)).collect(Collectors.toList());

यह आउटपुट [Sydney, New York, Mumbai, London, California, Amsterdam]

आप एक तुलनित्र Comparator.reverseOrder() का उपयोग कर सकते हैं एक तुलनित्र है जो प्राकृतिक आदेश के reverse लगाता है।

List<String> reverseSortedData = data.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());

आदिमों की धाराएँ

जावा विशेष प्रदान करता है Stream तीन पुरातन के प्रकार के लिए रों IntStream (के लिए int रों), LongStream (के लिए long ) और DoubleStream (के लिए double रों)। अपने संबंधित आदिम के लिए अनुकूलित कार्यान्वयन के अलावा, वे कई विशिष्ट टर्मिनल तरीके भी प्रदान करते हैं, आमतौर पर गणितीय कार्यों के लिए। उदाहरण के लिए:

IntStream is = IntStream.of(10, 20, 30);
double average = is.average().getAsDouble(); // average is 20.0

एक सरणी में एक स्ट्रीम के परिणाम ले लीजिए

एक Stream लिए Stream लिए एक संग्रह प्राप्त करने के लिए एनालॉग collect() एक सरणी Stream.toArray() विधि द्वारा प्राप्त किया जा सकता है:

List<String> fruits = Arrays.asList("apple", "banana", "pear", "kiwi", "orange");

String[] filteredFruits = fruits.stream()
    .filter(s -> s.contains("a"))
    .toArray(String[]::new);     

// prints: [apple, banana, pear, orange]
System.out.println(Arrays.toString(filteredFruits));

String[]::new एक विशेष प्रकार का विधि संदर्भ है: एक रचनाकार संदर्भ।

पहला तत्व ढूँढना जो एक विधेय से मेल खाता है

किसी शर्त से मेल खाने वाले Stream के पहले तत्व को ढूंढना संभव है।

इस उदाहरण के लिए, हमें पहला Integer मिलेगा जिसका वर्ग 50000 से अधिक है।

IntStream.iterate(1, i -> i + 1) // Generate an infinite stream 1,2,3,4...
    .filter(i -> (i*i) > 50000) // Filter to find elements where the square is >50000
    .findFirst(); // Find the first filtered element

यह अभिव्यक्ति परिणाम के साथ एक OptionalInt लौटाएगा।

ध्यान दें कि एक अनंत Stream , जावा प्रत्येक तत्व को तब तक जांचता रहेगा जब तक कि यह परिणाम न मिल जाए। एक परिमित Stream , यदि जावा तत्वों से बाहर निकलता है, लेकिन फिर भी परिणाम नहीं मिल पाता है, तो यह एक खाली OptionalInt

अनुक्रमणिका पर पुनरावृति करने के लिए IntStream का उपयोग करना

तत्वों की Stream एस आमतौर पर वर्तमान आइटम के सूचकांक मूल्य तक पहुंच की अनुमति नहीं देती है। अनुक्रमणिका तक पहुँच के दौरान किसी सरणी या ArrayList पर पुनरावृत्ति करने के लिए, IntStream.range(start, endExclusive)

String[] names = { "Jon", "Darin", "Bauke", "Hans", "Marc" };

IntStream.range(0, names.length)
    .mapToObj(i -> String.format("#%d %s", i + 1, names[i]))
    .forEach(System.out::println);

range(start, endExclusive) विधि एक और ÌntStream और mapToObj(mapper) String की एक धारा लौटाती है।

आउटपुट:

# 1 जॉन
# 2 डारिन
# 3 बाउक
# 4 हंस
# 5 मार्क

यह एक काउंटर के साथ लूप के for एक सामान्य का उपयोग करने के समान है, लेकिन पाइपलाइनिंग और समानांतर के लाभ के साथ:

for (int i = 0; i < names.length; i++) {
    String newName = String.format("#%d %s", i + 1, names[i]);
    System.out.println(newName);
}

फ्लैटपटन के साथ समतल धाराएं ()

बदले में सुव्यवस्थित करने वाली वस्तुओं की Stream को एक ही सतत Stream में समतल किया जा सकता है:

आइटम्स की सूची की सरणी को एकल सूची में परिवर्तित किया जा सकता है।

List<String> list1 =  Arrays.asList("one", "two");
      List<String> list2 =  Arrays.asList("three","four","five");
      List<String> list3 =  Arrays.asList("six");
          List<String> finalList = Stream.of(list1, list2, list3).flatMap(Collection::stream).collect(Collectors.toList());
System.out.println(finalList);

// [one, two, three, four, five, six]

मानों के रूप में आइटमों की सूची वाले मानचित्र को एक संयुक्त सूची में समतल किया जा सकता है

Map<String, List<Integer>> map = new LinkedHashMap<>();
map.put("a", Arrays.asList(1, 2, 3));
map.put("b", Arrays.asList(4, 5, 6));

List<Integer> allValues = map.values() // Collection<List<Integer>>
        .stream()                      // Stream<List<Integer>>
        .flatMap(List::stream)         // Stream<Integer>
        .collect(Collectors.toList());

System.out.println(allValues);
// [1, 2, 3, 4, 5, 6]

Map List को एक एकल सतत Stream में समतल किया जा सकता है

List<Map<String, String>> list = new ArrayList<>();
Map<String,String> map1 = new HashMap();
map1.put("1", "one");
map1.put("2", "two");

Map<String,String> map2 = new HashMap();
map2.put("3", "three");
map2.put("4", "four");
list.add(map1);
list.add(map2);


Set<String> output= list.stream()  //  Stream<Map<String, String>>
    .map(Map::values)              // Stream<List<String>>
    .flatMap(Collection::stream)   // Stream<String>
    .collect(Collectors.toSet());  //Set<String>
// [one, two, three,four]

स्ट्रीम के आधार पर मैप बनाएं

डुप्लीकेट चाबी के बिना सरल मामला

Stream<String> characters = Stream.of("A", "B", "C");

Map<Integer, String> map = characters
            .collect(Collectors.toMap(element -> element.hashCode(), element -> element));
// map = {65=A, 66=B, 67=C}

चीजों को अधिक घोषणात्मक बनाने के लिए, हम Function इंटरफ़ेस में Function विधि का उपयोग कर सकते हैं - Function.identity() । हम इस लंबो element -> element को बदल सकते हैं element -> element Function.identity() साथ element -> elementFunction.identity()

मामला जहाँ डुप्लिकेट कुंजियाँ हो सकती हैं

Collectors.toMap लिए javadoc.toMap राज्य:

यदि मैप की गई कुंजियों में डुप्लिकेट होते हैं ( Object.equals(Object) अनुसार), तो संग्रह ऑपरेशन किए जाने पर एक IllegalStateException को फेंक दिया जाता है। यदि मैप की गई कुंजियों में डुप्लिकेट हो सकते हैं, तो इसके बजाय toMap(Function, Function, BinaryOperator) उपयोग करें।

Stream<String> characters = Stream.of("A", "B", "B", "C");

Map<Integer, String> map = characters
            .collect(Collectors.toMap(
                element -> element.hashCode(),
                element -> element,
                (existingVal, newVal) -> (existingVal + newVal)));

// map = {65=A, 66=BB, 67=C}

Collectors.toMap(...) लिए दिया गया BinaryOperator Collectors.toMap(...) टकराव की स्थिति में संग्रहीत किए जाने वाले मूल्य को उत्पन्न करता है। यह:

  • पुराना मान लौटाएं, ताकि स्ट्रीम में पहला मूल्य पूर्वता ले,
  • नया मान लौटाएं, ताकि स्ट्रीम में अंतिम मूल्य पूर्वता ले, या
  • पुराने और नए मूल्यों को मिलाएं

मूल्य द्वारा समूहीकरण

जब आप एक डेटाबेस कैस्केड "समूह द्वारा" ऑपरेशन के बराबर प्रदर्शन करने की आवश्यकता होती है, तो आप Collectors.groupingBy उपयोग कर सकते हैं। वर्णन करने के लिए, निम्नलिखित एक नक्शा बनाता है जिसमें लोगों के नाम उपनामों के लिए मैप किए जाते हैं:

List<Person> people = Arrays.asList(
    new Person("Sam", "Rossi"),
    new Person("Sam", "Verdi"),
    new Person("John", "Bianchi"),
    new Person("John", "Rossi"),
    new Person("John", "Verdi")
);

Map<String, List<String>> map = people.stream()
        .collect(
                // function mapping input elements to keys
                Collectors.groupingBy(Person::getName, 
                // function mapping input elements to values,
                // how to store values
                Collectors.mapping(Person::getSurname, Collectors.toList()))
        );

// map = {John=[Bianchi, Rossi, Verdi], Sam=[Rossi, Verdi]}

Ideone पर रहते हैं

धाराओं का उपयोग करके यादृच्छिक स्ट्रिंग्स बनाना

यह कभी-कभी रैंडम Strings बनाने के लिए उपयोगी होता है, हो सकता है कि वेब-सेवा के लिए सत्र-आईडी या किसी एप्लिकेशन के लिए पंजीकरण के बाद प्रारंभिक पासवर्ड। इसे Stream एस का उपयोग करके आसानी से प्राप्त किया जा सकता है।

पहले हमें एक यादृच्छिक संख्या जनरेटर को इनिशियलाइज़ करना होगा। जनरेट किए गए String s के लिए सुरक्षा बढ़ाने के लिए, SecureRandom का उपयोग करना एक अच्छा विचार है।

नोट : एक SecureRandom बनाना काफी महंगा है, इसलिए इसे केवल एक बार ही करना सबसे अच्छा अभ्यास है और इसे फिर से शुरू करने के लिए समय-समय पर इसके setSeed() तरीकों में से एक को कॉल करें।

private static final SecureRandom rng = new SecureRandom(SecureRandom.generateSeed(20)); 
//20 Bytes as a seed is rather arbitrary, it is the number used in the JavaDoc example

यादृच्छिक String s बनाते समय, हम आमतौर पर केवल कुछ वर्णों (जैसे केवल अक्षर और अंक) का उपयोग करना चाहते हैं। इसलिए हम एक boolean लौटाने का एक तरीका बना सकते हैं जिसका उपयोग बाद में Stream को फ़िल्टर करने के लिए किया जा सकता है।

//returns true for all chars in 0-9, a-z and A-Z
boolean useThisCharacter(char c){
    //check for range to avoid using all unicode Letter (e.g. some chinese symbols)
    return c >= '0' && c <= 'z' && Character.isLetterOrDigit(c);
}

इसके बाद हम RNG का उपयोग विशिष्ट लंबाई की एक यादृच्छिक स्ट्रिंग को उत्पन्न करने के लिए कर सकते हैं जिसमें चारसेट होते हैं जो हमारे useThisCharacter पार करते हैं।

public String generateRandomString(long length){
    //Since there is no native CharStream, we use an IntStream instead
    //and convert it to a Stream<Character> using mapToObj.
    //We need to specify the boundaries for the int values to ensure they can safely be cast to char
    Stream<Character> randomCharStream = rng.ints(Character.MIN_CODE_POINT, Character.MAX_CODE_POINT).mapToObj(i -> (char)i).filter(c -> this::useThisCharacter).limit(length);

    //now we can use this Stream to build a String utilizing the collect method.
    String randomString = randomCharStream.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString();
    return randomString;
}

गणितीय क्रियाओं को लागू करने के लिए धाराओं का उपयोग करना

Stream s, और विशेष रूप से IntStream s, IntStream पद (।) को लागू करने का एक सुंदर तरीका है। Stream की श्रेणियों का उपयोग योग की सीमा के रूप में किया जा सकता है।

जैसे, पिहा का माधव का अनुमान सूत्र द्वारा दिया गया है (स्रोत: विकिपीडिया ): माधव का अंदाज

यह एक मनमाना परिशुद्धता के साथ गणना की जा सकती है। जैसे, 101 पद के लिए:

double pi = Math.sqrt(12) * 
            IntStream.rangeClosed(0, 100)
                     .mapToDouble(k -> Math.pow(-3, -1 * k) / (2 * k + 1))
                     .sum();

नोट: double परिशुद्धता के साथ, 29 के ऊपरी हिस्से का चयन करने के लिए पर्याप्त परिणाम प्राप्त करना पर्याप्त है जो Math.Pi से अप्रभेद्य है।

स्व-दस्तावेजी प्रक्रियाओं को लिखने के लिए धाराओं और विधि सन्दर्भों का उपयोग करना

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

public interface Ordered {
    default int getOrder(){
        return 0;
    }
}

public interface Valued<V extends Ordered> {
    boolean hasPropertyTwo();
    V getValue();
}

public interface Thing<V extends Ordered> {
    boolean hasPropertyOne();
    Valued<V> getValuedProperty();
}

public <V extends Ordered> List<V> myMethod(List<Thing<V>> things) {
    List<V> results = new ArrayList<V>();
    for (Thing<V> thing : things) {
        if (thing.hasPropertyOne()) {
            Valued<V> valued = thing.getValuedProperty();
            if (valued != null && valued.hasPropertyTwo()){
                V value = valued.getValue();
                if (value != null){
                    results.add(value);
                }
            }
        }
    }
    results.sort((a, b)->{
        return Integer.compare(a.getOrder(), b.getOrder());
    });
    return results;
}

Stream एस और विधि संदर्भों का उपयोग करके फिर से लिखी गई यह अंतिम विधि बहुत अधिक सुपाठ्य है और प्रक्रिया का प्रत्येक चरण जल्दी और आसानी से समझा जाता है - यह सिर्फ छोटा नहीं है, यह एक नज़र में भी दिखाता है कि प्रत्येक चरण में कोड के लिए इंटरफेस और कक्षाएं जिम्मेदार हैं:

public <V extends Ordered> List<V> myMethod(List<Thing<V>> things) {
    return things.stream()
        .filter(Thing::hasPropertyOne)
        .map(Thing::getValuedProperty)
        .filter(Objects::nonNull)
        .filter(Valued::hasPropertyTwo)
        .map(Valued::getValue)
        .filter(Objects::nonNull)
        .sorted(Comparator.comparing(Ordered::getOrder))
        .collect(Collectors.toList());
}    

मैपिंग की धाराओं का उपयोग करना। मैपिंग के बाद प्रारंभिक मानों को संरक्षित करने के लिए प्रयास करें

जब आपके पास एक Stream जिसे आपको मैप करने की आवश्यकता होती है, लेकिन साथ ही प्रारंभिक मानों को संरक्षित करना चाहते हैं, तो आप Stream को मैप पर मैप कर सकते हैं। Map.Entry<K,V> निम्न की तरह एक उपयोगिता पद्धति का उपयोग कर:

public static <K, V> Function<K, Map.Entry<K, V>> entryMapper(Function<K, V> mapper){
    return (k)->new AbstractMap.SimpleEntry<>(k, mapper.apply(k));
}

तब आप अपने कनवर्टर का उपयोग कर सकते हैं Stream करने के लिए मूल और मैप किए गए दोनों मानों तक पहुंच:

Set<K> mySet;
Function<K, V> transformer = SomeClass::transformerMethod;
Stream<Map.Entry<K, V>> entryStream = mySet.stream()
    .map(entryMapper(transformer));

फिर आप उस Stream को सामान्य रूप से प्रोसेस करना जारी रख सकते हैं। यह एक मध्यवर्ती संग्रह बनाने के ओवरहेड से बचा जाता है।

स्ट्रीम संचालन श्रेणियां

स्ट्रीम ऑपरेशन दो मुख्य श्रेणियों, मध्यवर्ती और टर्मिनल संचालन, और दो उप-श्रेणियों, स्टेटलेस और स्टेटफुल में आते हैं।


मध्यवर्ती संचालन:

एक मध्यवर्ती ऑपरेशन हमेशा आलसी होता है , जैसे कि एक सरल Stream.map । यह तब तक लागू नहीं किया जाता है जब तक कि धारा वास्तव में खपत नहीं होती है। इसे आसानी से सत्यापित किया जा सकता है:

Arrays.asList(1, 2 ,3).stream().map(i -> {
    throw new RuntimeException("not gonna happen");
    return i;
});

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


टर्मिनल संचालन

टर्मिनल ऑपरेशंस एक स्ट्रीम की खपत को ट्रिगर करते हैं। अधिक सामान्य में से कुछ Stream.forEach या Stream.collect । उन्हें आमतौर पर मध्यवर्ती संचालन की श्रृंखला के बाद रखा जाता है और लगभग हमेशा उत्सुक रहते हैं


स्टेटलेस ऑपरेशन

स्टेटलेसनेस का अर्थ है कि प्रत्येक आइटम को अन्य वस्तुओं के संदर्भ के बिना संसाधित किया जाता है। स्टेटलेस ऑपरेशन स्ट्रीम की मेमोरी-इफेक्टिव प्रोसेसिंग की अनुमति देते हैं। Stream.map और Stream.filter जैसे ऑपरेशन जिन्हें स्ट्रीम के अन्य मदों की जानकारी की आवश्यकता नहीं है, उन्हें स्टेटलेस माना जाता है।


राज्य संचालन

स्टेटफुलनेस का मतलब है कि प्रत्येक आइटम पर कार्रवाई धारा के (कुछ) अन्य मदों पर निर्भर करती है। इसके लिए एक राज्य को संरक्षित करने की आवश्यकता है। लंबे समय तक, या अनंत, धाराओं के साथ राज्य-संचालन के संचालन टूट सकते हैं। की तरह संचालन Stream.sorted की आवश्यकता होती है धारा के पूरी तरह से पहले किसी भी आइटम उत्सर्जित होता है जो मदों की एक काफी लंबे समय धारा में टूट जाएगा संसाधित करने के लिए। इसे एक लंबी धारा ( अपने जोखिम पर चलाएं ) द्वारा प्रदर्शित किया जा सकता है:

// works - stateless stream
long BIG_ENOUGH_NUMBER = 999999999;
IntStream.iterate(0, i -> i + 1).limit(BIG_ENOUGH_NUMBER).forEach(System.out::println);

यह Stream.sorted आउट की स्मृति के कारण आउट-ऑफ-मेमोरी का कारण बन जाएगा।

// Out of memory - stateful stream
IntStream.iterate(0, i -> i + 1).limit(BIG_ENOUGH_NUMBER).sorted().forEach(System.out::println);

एक पुनरावृत्त को एक धारा में बदलना

Spliterators.spliterator() या Spliterators.spliteratorUnknownSize() का उपयोग करके एक धारा के लिए एक Spliterators.spliteratorUnknownSize() बदलने के लिए:

Iterator<String> iterator = Arrays.asList("A", "B", "C").iterator();    
Spliterator<String> spliterator = Spliterators.spliteratorUnknownSize(iterator, 0);
Stream<String> stream = StreamSupport.stream(spliterator, false);

धाराओं के साथ कमी

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

IntStream का sum() विधि एक कमी का एक उदाहरण है; यह स्ट्रीम के हर शब्द के अतिरिक्त लागू होता है, जिसके परिणामस्वरूप एक अंतिम मूल्य होता है: घट घट

यह (((1+2)+3)+4) बराबर है

एक स्ट्रीम की reduce विधि एक कस्टम कमी बनाने के लिए अनुमति देता है। sum() विधि को लागू reduce लिए reduce विधि का उपयोग करना संभव है:

IntStream istr;
    
//Initialize istr
    
OptionalInt istr.reduce((a,b)->a+b);

Optional संस्करण को लौटा दिया जाता है ताकि खाली धाराओं को उचित तरीके से संभाला जा सके।

कमी का एक और उदाहरण एक संयोजन है Stream<LinkedList<T>> एक एकल में LinkedList<T> :

Stream<LinkedList<T>> listStream;
    
//Create a Stream<LinkedList<T>>
    
Optional<LinkedList<T>> bigList = listStream.reduce((LinkedList<T> list1, LinkedList<T> list2)->{
    LinkedList<T> retList = new LinkedList<T>();
    retList.addAll(list1);
    retList.addAll(list2);
    return retList;
});

आप एक पहचान तत्व भी प्रदान कर सकते हैं। उदाहरण के लिए, अतिरिक्त के लिए पहचान तत्व 0 है, x+0==x । गुणन के लिए, पहचान तत्व 1 है, x*1==x । उपरोक्त मामले में, पहचान तत्व एक खाली LinkedList<T> , क्योंकि यदि आप किसी अन्य सूची में एक खाली सूची जोड़ते हैं, तो जिस सूची को आप "जोड़" रहे हैं वह नहीं बदलती है:

Stream<LinkedList<T>> listStream;

//Create a Stream<LinkedList<T>>

LinkedList<T> bigList = listStream.reduce(new LinkedList<T>(), (LinkedList<T> list1, LinkedList<T> list2)->{
    LinkedList<T> retList = new LinkedList<T>();
    retList.addAll(list1);
    retList.addAll(list2);
    return retList;
});

ध्यान दें कि जब कोई पहचान तत्व प्रदान किया जाता है, तो रिटर्न वैल्यू एक Optional धारा में नहीं लिपटी होती है, जिसे खाली स्ट्रीम पर बुलाया जाता है, reduce() पहचान तत्व को वापस कर देगा।

बाइनरी ऑपरेटर को भी सहयोगी होना चाहिए, जिसका अर्थ है कि (a+b)+c==a+(b+c) । ऐसा इसलिए है क्योंकि तत्वों को किसी भी क्रम में कम किया जा सकता है। उदाहरण के लिए, उपरोक्त जोड़ में कमी इस तरह की जा सकती है:

अन्य राशि में कमी

यह कमी लिखने के बराबर है ((1+2)+(3+4)) । सहानुभूति की संपत्ति भी जावा को Stream को समानांतर में कम करने की अनुमति देती है - Stream एक हिस्से को प्रत्येक प्रोसेसर द्वारा कम किया जा सकता है, जिसके अंत में प्रत्येक प्रोसेसर के परिणाम के संयोजन में कमी आती है।

एक स्ट्रिंग में एक धारा में शामिल होना

एक उपयोग का मामला जो अक्सर सामने आता है, एक धारा से एक String बना रहा है, जहां धारा-आइटम एक निश्चित चरित्र द्वारा अलग किए जाते हैं। Collectors.joining() विधि का उपयोग इसके लिए किया जा सकता है, जैसे निम्नलिखित उदाहरण में:

Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");

String result = fruitStream.filter(s -> s.contains("a"))
           .map(String::toUpperCase)
           .sorted()
           .collect(Collectors.joining(", "));
           
System.out.println(result);

आउटपुट:

सेब, बन्ना, ऑरेंज, PEAR

Collectors.joining() । जॉइनिंग Collectors.joining() विधि पूर्व और उपसर्गों के लिए भी पूरा कर सकते हैं:

String result = fruitStream.filter(s -> s.contains("e"))
           .map(String::toUpperCase)
           .sorted()
           .collect(Collectors.joining(", ", "Fruits: ", "."));
           
System.out.println(result);

आउटपुट:

फल: सेब, ऑरेंज, PEAR।

Ideone पर रहते हैं



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow