Java Language
स्ट्रीम
खोज…
परिचय
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);
आउटपुट:
सेब
केला
संतरा
नाशपाती
उपरोक्त कोड द्वारा किए गए कार्यों को संक्षेप में प्रस्तुत किया जा सकता है:
एक
Stream<String>
बनाएँStream<String>
स्थिर फैक्ट्री विधिStream.of(values)
का उपयोग करके क्रमबद्ध ऑर्डर किए गए फलों केString
युक्तStream
।filter()
ऑपरेशन केवल उन्हीं तत्वों को बनाए रखता है जो किसी दिए गए विधेय से मेल खाते हैं (वे तत्व जो कि विधेय द्वारा जांचे जाने पर वापस लौटते हैं)। इस मामले में, यह"a"
वाले तत्वों को बरकरार रखता है। विधेय को लंबोदर अभिव्यक्ति के रूप में दिया जाता है।map()
ऑपरेशन दिए गए फ़ंक्शन का उपयोग करके प्रत्येक तत्व को बदल देता है, जिसे मैपर कहा जाता है। इस स्थिति में, प्रत्येक फलString
को विधि-संदर्भString::toUppercase
का उपयोग करके इसके अपरकेसString
संस्करण में मैप किया जाता है।ध्यान दें कि यदि मैपिंग फ़ंक्शन अपने इनपुट पैरामीटर के लिए एक अलग प्रकार देता है तो
map()
ऑपरेशन एक अलग जेनेरिक प्रकार के साथ एक धारा लौटाएगा। उदाहरण के लिएStream<String>
कॉलिंग.map(String::isEmpty)
Stream<Boolean>
sorted()
ऑपरेशनStream
के तत्वों को उनकी प्राकृतिक ऑर्डरिंग के अनुसार क्रमबद्ध करता है (शाब्दिक रूप सेString
के मामले में)।अंत में,
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
समानांतर मोड कई कोर पर कई थ्रेड्स के उपयोग की अनुमति देता है, लेकिन इसमें उस आदेश की कोई गारंटी नहीं है जिसमें तत्वों को संसाधित किया जाता है।
यदि कई विधियों को अनुक्रमिक Stream
पर बुलाया जाता है, तो हर विधि को लागू नहीं करना पड़ता है। उदाहरण के लिए, अगर एक Stream
फ़िल्टर और है तत्वों की संख्या एक, इस तरह के रूप में एक विधि के लिए बाद में एक कॉल करने के लिए कम हो जाता है sort
घटित नहीं होगा। यह एक अनुक्रमिक Stream
के प्रदर्शन को बढ़ा सकता है - एक अनुकूलन जो समानांतर Stream
साथ संभव नहीं है।
उदाहरण:
// parallel
long howManyOddNumbersParallel = integerList.parallelStream()
.filter(e -> (e % 2) == 1)
.count();
System.out.println(howManyOddNumbersParallel); // Output: 2
कंटेनरों से अंतर (या संग्रह )
जबकि कंटेनरों और धाराओं दोनों पर कुछ क्रियाएं की जा सकती हैं, वे अंततः विभिन्न उद्देश्यों की पूर्ति करते हैं और विभिन्न कार्यों का समर्थन करते हैं। कंटेनर अधिक ध्यान केंद्रित करते हैं कि तत्वों को कैसे संग्रहीत किया जाता है और उन तत्वों को कैसे कुशलता से एक्सेस किया जा सकता है। दूसरी ओर, एक 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);
यह एक वैध टर्मिनल ऑपरेशन के साथ Stream
अनुक्रम है, इस प्रकार एक आउटपुट का उत्पादन होता है।
तुम भी इस्तेमाल कर सकते हैं forEach
के बजाय peek
:
IntStream.range(1, 10).filter(a -> a % 2 == 0).forEach(System.out::println);
आउटपुट:
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()");
}
आउटपुट:
हमें 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
जैसे आंकड़े एकत्र करने के लिए एक राज्य वस्तु प्रदान करती हैं।
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);
जिसके परिणामस्वरूप होगा:
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 -> element
। Function.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]}
धाराओं का उपयोग करके यादृच्छिक स्ट्रिंग्स बनाना
यह कभी-कभी रैंडम 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।