Java 8 में मैप () और फ्लैटपाइप () के तरीकों में क्या अंतर है?


708

जावा 8 में, तरीकों Stream.map()और Stream.flatMap()तरीकों के बीच अंतर क्या है ?


55
सिग्नेचर टाइप थोमा पूरी कहानी बताता है। map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R
क्रिस मार्टिन

99
fwiw, उन प्रकार के हस्ताक्षर जावा की तरह भी नहीं दिखते हैं। (मुझे पता है, मुझे पता है - लेकिन यह कहना है कि "पूरी कहानी" wrt मैप / फ्लैटपाइप नए और बेहतर "जावा ++" के बारे में बहुत सारे ज्ञान मानता है)
माइकल

16
@michael उस प्रकार का हस्ताक्षर हास्केल जैसा दिखता है, न कि जावा। लेकिन यह स्पष्ट नहीं है कि क्या वास्तविक जावा हस्ताक्षर किसी भी अधिक पठनीय है: <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
स्टुअर्ट मार्क्स

8
हा, हाँ, मैं "वास्तविक जावा" की बात कर रहा था। C ++ की तरह, आधुनिक जावा लगभग किसी के लिए भी अपरिचित नहीं है, जिसने इसे 90 के दशक में उपयोग करना शुरू कर दिया (जैसे मैंने किया, दोनों भाषाओं में)। केवल टिप्पणी का जवाब देते हुए, वह विधि हस्ताक्षर शायद ही एक "पूरी कहानी" बताता है, कम से कम अब और नहीं, अतिरिक्त प्रदर्शनी के बिना (या उस टिप्पणीकार मामले, अनुवाद में)।
माइकल

2
जो कहना है, एक mapमैपर लैम्ब्डा रिटर्न R, एक flatMapमैपर लैम्ब्डा एक ( ) Streamका रिटर्न देता है । मैपर द्वारा लौटाई गई धाराओं को प्रभावी ढंग से समेटा गया है। अन्यथा, दोनों और वापस ; अंतर यह है कि मैपर लैम्ब्डा वापस क्या है, बनाम । RStream<R>flatMapmapflatMapStream<R>RStream<R>
derekm

जवाबों:


816

दोनों mapऔर flatMapएक करने के लिए लागू किया जा सकता Stream<T>है और वे दोनों वापसी एक Stream<R>। अंतर यह है कि mapऑपरेशन प्रत्येक इनपुट मूल्य के लिए एक आउटपुट मूल्य का उत्पादन करता है, जबकि flatMapऑपरेशन प्रत्येक इनपुट मूल्य के लिए एक मनमाना संख्या (शून्य या अधिक) मान पैदा करता है।

यह प्रत्येक ऑपरेशन के तर्कों में परिलक्षित होता है।

mapआपरेशन एक लेता है Functionजो इनपुट धारा में प्रत्येक मान के लिए कहा जाता है, और एक परिणाम मूल्य है, जो उत्पादन धारा को भेजा जाता है पैदा करता है।

flatMapआपरेशन एक समारोह है कि धारणात्मक एक मूल्य की खपत और मूल्यों के एक मनमाना संख्या का उत्पादन करना चाहता है लेता है। हालाँकि, जावा में, यह एक मनमाने ढंग से मानों की संख्या वापस करने के लिए एक विधि के लिए बोझिल है, क्योंकि विधियाँ केवल शून्य या एक मान वापस कर सकती हैं। कोई एक एपीआई की कल्पना कर सकता है जहां मैपर फ़ंक्शन flatMapमान लेता है और एक सरणी या ए देता हैListमान, जो तब आउटपुट पर भेजे जाते हैं। यह देखते हुए कि यह स्ट्रीम लाइब्रेरी है, विशेष रूप से उपयुक्त मानों की एक मनमानी संख्या का प्रतिनिधित्व करने के लिए उपयुक्त तरीका है कि मैपर फ़ंक्शन एक स्ट्रीम को वापस करने के लिए है! मैपर द्वारा लौटाए गए स्ट्रीम से मान स्ट्रीम से निकाले जाते हैं और आउटपुट स्ट्रीम में दिए जाते हैं। मैपर फ़ंक्शन के लिए प्रत्येक कॉल द्वारा लौटाए गए मानों का "क्लैंप" आउटपुट स्ट्रीम में बिल्कुल भी प्रतिष्ठित नहीं है, इस प्रकार आउटपुट को "चपटा" कहा जाता है।

विशिष्ट उपयोग मैपर फ़ंक्शन के flatMapलिए है Stream.empty()यदि यह शून्य मान भेजना चाहता है, या ऐसा कुछ है जैसे Stream.of(a, b, c)कि यह कई मान वापस करना चाहता है। लेकिन निश्चित रूप से किसी भी धारा को वापस किया जा सकता है।


26
मुझे लगता है जैसे flatMapऑपरेशन फ्लैट के ठीक विपरीत है। फिर भी, यह कंप्यूटर वैज्ञानिकों के लिए छोड़ दो कि यह सिर पर एक शब्द है। एक फ़ंक्शन के "पारदर्शी" होने का अर्थ है कि आप ऐसा कुछ भी नहीं देख सकते हैं, यह सिर्फ परिणाम है, जबकि बोलचाल की भाषा में आप पारदर्शी होने की प्रक्रिया चाहते हैं इसका मतलब है कि आप इसे देखना चाहते हैं।
कोलैडिक्ट

45
@coladict इसे एक अलग नजरिए से देखने की कोशिश करें: यह पारदर्शी मामला नहीं है, जहां आप भीतर के कामकाज को देख सकते हैं, बल्कि पूरा कार्य स्वयं पारदर्शी है, अर्थात आपके लिए, - फिर भी अपना काम करते हुए और आपको जो दिखता है, उसे देखते हुए साथ काम कर रहे हैं। इस मामले में, "फ्लैट" "नेस्टेड" के विपरीत को संदर्भित करता है, फ्लैटमैप एक समतल स्तर को समतल करके हटाता है।
जीफिरो

7
@coladict "पारदर्शी" बात मेरे सिर को सालों से खा रही है। कम से कम एक अन्य व्यक्ति को जानने का खुशी उसी तरह महसूस होती है।
अशोक बिजॉय देबनाथ

9
चपटेपन को 2-स्तरीय संरचना को एकल स्तरीय संरचना में बदलने से आता है, उदाहरण के लिए Dici का उत्तर देखें stackoverflow.com/a/26684582/6012102
andrzej.szmukala

26
यह फ्लैटपाइप का सबसे अच्छा विवरण है । यह वही है जो इसे क्लिक करता है: मैपर द्वारा लौटाए गए स्ट्रीम से मान स्ट्रीम से हटा दिए जाते हैं और आउटपुट स्ट्रीम में पास हो जाते हैं। मैपर फ़ंक्शन के लिए प्रत्येक कॉल द्वारा लौटाए गए मानों का "क्लैंप" आउटपुट स्ट्रीम में बिल्कुल भी प्रतिष्ठित नहीं होता है, इस प्रकार आउटपुट को "चपटा" कहा जाता है । धन्यवाद!
नीवेक

465

Stream.flatMap, जैसा कि इसके नाम से अनुमान लगाया जा सकता है, एक mapऔर एक flatऑपरेशन का संयोजन है । इसका मतलब है कि आप पहले अपने तत्वों पर एक फ़ंक्शन लागू करते हैं, और फिर इसे समतल करते हैं। Stream.mapकेवल स्ट्रीम को समतल किए बिना स्ट्रीम में फ़ंक्शन लागू करता है।

यह समझने के लिए कि किस धारा में समतल होना शामिल है, एक संरचना पर विचार करें, [ [1,2,3],[4,5,6],[7,8,9] ]जिसमें "दो स्तर" हैं। इसे समतल करने का अर्थ है इसे "एक स्तर" संरचना में बदलना [ 1,2,3,4,5,6,7,8,9 ]:।


6
सरल और मधुर
Bluelurker

3
हाहा, निष्पक्ष होने के लिए मैं अभी भी आश्चर्यचकित हूं कि यह प्रश्न कितना ट्रैफ़िक प्राप्त करता है। एक और मज़ेदार अवलोकन यह है कि यह लगभग 5 साल हो गया है मैंने यह जवाब लिखा है और उत्थान का एक काफी सुसंगत पैटर्न है जहां स्वीकार किए गए उत्तर को मेरे प्रत्येक उत्तर के लिए लगभग दो upvotes मिलते हैं। यह आश्चर्यजनक रूप से सुसंगत है।
डिसी

1
यह कैसे स्वीकृत उत्तर नहीं है, इस बिंदु पर सीधे आने के लिए धन्यवाद और एक बहुत ही सरल उदाहरण दिया
1mike12

234

मैं अधिक व्यावहारिक दृष्टिकोण प्राप्त करने के लिए 2 उदाहरण देना चाहूंगा :
पहला उदाहरण मानचित्र का उपयोग करना:

@Test
public void convertStringToUpperCaseStreams() {
    List<String> collected = Stream.of("a", "b", "hello") // Stream of String 
            .map(String::toUpperCase) // Returns a stream consisting of the results of applying the given function to the elements of this stream.
            .collect(Collectors.toList());
    assertEquals(asList("A", "B", "HELLO"), collected);
}   

पहले उदाहरण में कुछ भी विशेष नहीं है, अपरकेस में Functionवापस आने के लिए एक आवेदन किया गया है String

दूसरा उदाहरण flatMap:

@Test
public void testflatMap() throws Exception {
    List<Integer> together = Stream.of(asList(1, 2), asList(3, 4)) // Stream of List<Integer>
            .flatMap(List::stream)
            .map(integer -> integer + 1)
            .collect(Collectors.toList());
    assertEquals(asList(2, 3, 4, 5), together);
}

दूसरे उदाहरण में, एक स्ट्रीम ऑफ़ लिस्ट पास की गई है। यह इंटेगर की एक स्ट्रीम नहीं है!
यदि किसी ट्रांसफ़ॉर्मेशन फंक्शन का उपयोग (मानचित्र के माध्यम से) किया जाता है, तो पहले स्ट्रीम को कुछ और (एक स्ट्रीम ऑफ़ इंटेक्स) के रूप में समतल करना होगा।
यदि फ्लैटपाइप हटा दिया जाता है, तो निम्न त्रुटि वापस आ जाती है: ऑपरेटर + तर्क प्रकार (ओं) की सूची के लिए अपरिभाषित है, int।
इंटेगर की सूची पर + 1 लागू करना संभव नहीं है!


@PrashanthDebbadwar मुझे लगता है कि आप एक स्ट्रीम के Stream<Integer>बजाय एक स्ट्रीम के साथ समाप्त होगा Integer
पायने

166

कृपया एक स्पष्ट विचार प्राप्त करने के लिए पूरी तरह से पोस्ट के माध्यम से जाएं,

नक्शा बनाम फ्लैट मैप:

किसी सूची से प्रत्येक शब्द की लंबाई लौटाने के लिए, हम नीचे कुछ ऐसा करेंगे ..

नीचे दिया गया लघु संस्करण

जब हम दो सूचियाँ एकत्र करते हैं, तो नीचे दी गई हैं

बिना सपाट नक्शा => [1,2], [1,1] => [[1,2], [1,1]] यहां दो सूचियों को एक सूची के अंदर रखा गया है, इसलिए उत्पादन सूची युक्त सूची होगी

फ्लैट मैप के साथ => [1,2], [1,1] => [1,2,1,1] यहाँ दो सूचियाँ चपटी हैं और केवल मान सूची में रखे गए हैं, इसलिए आउटपुट केवल तत्वों से युक्त सूची होगी

मूल रूप से यह सभी वस्तुओं को एक में मिला देता है

## विस्तृत संस्करण नीचे दिया गया है: -

उदाहरण के लिए: -
एक सूची ["STACK", "OOOVVVER"] पर विचार करें और हम ["STACKOVER"] जैसी सूची वापस करने का प्रयास कर रहे हैं (उस सूची से केवल अद्वितीय अक्षर लौटाते हुए) प्रारंभ में, हम नीचे लौटने के लिए कुछ इस तरह करेंगे। सूची [ "STACKOVER"] से [ "स्टैक", "OOOVVVER"]

public class WordMap {
  public static void main(String[] args) {
    List<String> lst = Arrays.asList("STACK","OOOVER");
    lst.stream().map(w->w.split("")).distinct().collect(Collectors.toList());
  }
}

यहाँ मुद्दा यह है कि, लैम्बडा ने मानचित्र विधि को पारित कर दिया है, प्रत्येक शब्द के लिए एक स्ट्रिंग सरणी देता है, इसलिए मानचित्र विधि द्वारा दी गई धारा वास्तव में स्ट्रीम की होती है, लेकिन जो हमें चाहिए वह है वर्णों की एक धारा का प्रतिनिधित्व करने के लिए स्ट्रीम, नीचे दी गई छवि दर्शाती है मुसीबत।

चित्र A:

यहाँ छवि विवरण दर्ज करें

आप सोच सकते हैं कि, हम फ़्लैटमैप का उपयोग करके इस समस्या को हल कर सकते हैं,
ठीक है, आइए देखें कि मानचित्र और Arrays.stream का उपयोग करके इसे कैसे हल किया जाए सबसे पहले आपको सरणियों की एक धारा के बजाय पात्रों की एक धारा की आवश्यकता है। Arrays.stream नामक एक विधि है (जो उदाहरण के लिए एक सरणी लेगी और एक स्ट्रीम तैयार करेगी:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .map(Arrays::stream).distinct() //Make array in to separate stream
    .collect(Collectors.toList());

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

फ्लैटपाइप का उपयोग करके हम इस समस्या को नीचे दिए अनुसार ठीक कर सकते हैं:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .flatMap(Arrays::stream).distinct() //flattens each generated stream in to a single stream
    .collect(Collectors.toList());

flatMap प्रत्येक सरणी को स्ट्रीम के साथ नहीं, बल्कि उस स्ट्रीम की सामग्री के साथ मैपिंग करेगा। नक्शे का उपयोग करते समय उत्पन्न होने वाली सभी व्यक्तिगत धाराएँ (Arrays :: स्ट्रीम) एक एकल स्ट्रीम में विलय हो जाती हैं। चित्रा बी फ्लैटपाइप विधि का उपयोग करने के प्रभाव को दिखाता है। चित्र ए। चित्रा बी में मानचित्र क्या करता है, इसकी तुलना करें यहाँ छवि विवरण दर्ज करें

फ्लैटपाइप विधि आपको एक धारा के प्रत्येक मान को दूसरी धारा के साथ बदलने देती है और फिर सभी उत्पन्न धाराओं को एक धारा में जोड़ देती है।


2
अच्छी आरेखीय व्याख्या।
हितेश

108

एक पंक्ति उत्तर: flatMapएक Collection<Collection<T>>में समतल करने में मदद करता हैCollection<T> । उसी तरह, यह भी एक समतल जाएगा Optional<Optional<T>>में Optional<T>

यहाँ छवि विवरण दर्ज करें

जैसा कि आप देख सकते हैं, map()केवल साथ :

  • मध्यवर्ती प्रकार है Stream<List<Item>>
  • वापसी का प्रकार है List<List<Item>>

और इसके साथ flatMap():

  • मध्यवर्ती प्रकार है Stream<Item>
  • वापसी का प्रकार है List<Item>

यह नीचे दिए गए कोड से परीक्षा परिणाम है:

-------- Without flatMap() -------------------------------
     collect() returns: [[Laptop, Phone], [Mouse, Keyboard]]

-------- With flatMap() ----------------------------------
     collect() returns: [Laptop, Phone, Mouse, Keyboard]

उपयोग किया गया कोड :

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class Parcel {
  String name;
  List<String> items;

  public Parcel(String name, String... items) {
    this.name = name;
    this.items = Arrays.asList(items);
  }

  public List<String> getItems() {
    return items;
  }

  public static void main(String[] args) {
    Parcel amazon = new Parcel("amazon", "Laptop", "Phone");
    Parcel ebay = new Parcel("ebay", "Mouse", "Keyboard");
    List<Parcel> parcels = Arrays.asList(amazon, ebay);

    System.out.println("-------- Without flatMap() ---------------------------");
    List<List<String>> mapReturn = parcels.stream()
      .map(Parcel::getItems)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + mapReturn);

    System.out.println("\n-------- With flatMap() ------------------------------");
    List<String> flatMapReturn = parcels.stream()
      .map(Parcel::getItems)
      .flatMap(Collection::stream)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + flatMapReturn);
  }
}

8
बहुत कुरकुरा उदाहरण .., ​​अपने उदाहरण के साथ अवधारणा को समझने के लिए कुछ सेकंड से ज्यादा नहीं लगेगा ...
TechDog

2
अच्छी व्याख्या। वास्तव में सरल और सबसे अच्छी व्याख्या के लिए सराहना करते हैं
सचिन राणे

42

जिस फ़ंक्शन को आप पास करते हैं, stream.mapउसे एक ऑब्जेक्ट वापस करना होगा। इसका मतलब है कि इनपुट स्ट्रीम में प्रत्येक ऑब्जेक्ट आउटपुट स्ट्रीम में बिल्कुल एक ऑब्जेक्ट में परिणाम करता है।

फ़ंक्शन जो आप पास करते हैं, stream.flatMapवह प्रत्येक ऑब्जेक्ट के लिए एक स्ट्रीम देता है। इसका मतलब है कि फ़ंक्शन प्रत्येक इनपुट ऑब्जेक्ट (कोई नहीं सहित) के लिए किसी भी संख्या में ऑब्जेक्ट वापस कर सकता है। परिणामी धाराएँ तब एक आउटपुट स्ट्रीम तक पहुंच जाती हैं।


आप "प्रत्येक इनपुट ऑब्जेक्ट (कोई भी नहीं) के लिए" किसी भी संख्या में वस्तुओं को वापस क्यों करना चाहेंगे?
डेरेक महार

4
@DerekMahar इसके लिए बहुत सारे उपयोग-मामले होंगे। उदाहरण के लिए, मान लीजिए कि Departmentआपके संगठन में आपकी एक धारा है । प्रत्येक विभाग में 0 और n के बीच है Employee। आपको जो भी चाहिए वह सभी कर्मचारियों की एक धारा है। तो तुम क्या करते हो? आप एक फ्लैटपाइप विधि लिखते हैं जो एक विभाग लेता है और अपने कर्मचारियों की एक धारा लौटाता है।
फिलिप

फिलिप, क्या आपका उदाहरण उपयोग करने का मुख्य कारण बताता है flatMap? मुझे संदेह है कि यह आकस्मिक हो सकता है और प्रमुख उपयोग के मामले या कारण का वर्णन नहीं करता है कि क्यों flatMapमौजूद है। (नीचे जारी ...)
डेरेक महार

Dzone.com/articles/understanding-flatmap को पढ़ने के बाद , मुझे लगता है कि पीछे मुख्य प्रेरणा flatMapत्रुटियों को समायोजित करना है जो उपयोग करते समय मौजूद होगी map। आप उन मामलों को कैसे संभालते हैं जहां मूल सेट में एक या अधिक आइटम को आउटपुट आइटम पर मैप नहीं किया जा सकता है? प्रत्येक इनपुट ऑब्जेक्ट के लिए एक इंटरमीडिएट सेट (कहो Optionalया कहो Stream) शुरू करके , flatMapआप "अमान्य" इनपुट ऑब्जेक्ट्स (या तथाकथित "खराब सेब" को stackoverflow.com/a/52248643/107158 से अलग कर सकते हैं) अंतिम सेट।
डेरेक महार

1
@DerekMahar हां, ऐसे स्ट्यूशन जहां प्रत्येक इनपुट-ऑब्जेक्ट वापस आ सकता है या आउटपुट-ऑब्जेक्ट वापस नहीं कर सकता है, फ्लैट-मैप के लिए एक और अच्छा उपयोग-मामला है।
फिलिप

29

मानचित्र के लिए हमारे पास तत्वों की एक सूची और एक (फ़ंक्शन, एक्शन) f है:

[a,b,c] f(x) => [f(a),f(b),f(c)]

और फ्लैट के नक्शे के लिए हमारे पास तत्वों की एक सूची है और हमारे पास एक (फ़ंक्शन, एक्शन) एफ है और हम चाहते हैं कि परिणाम थोड़ा हटके हो:

[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]

25

मुझे लगता है कि यहाँ ज्यादातर उत्तर सरल समस्या को पूरा करते हैं। यदि आप पहले से ही समझते हैं कि कैसे mapकाम करता है जो समझ में आसान होना चाहिए।

ऐसे मामले हैं जहां हम उपयोग करते समय अवांछित नेस्टेड संरचनाओं के साथ समाप्त हो सकते हैं map(), इस flatMap()विधि को लपेटकर बचने से इसे दूर करने के लिए डिज़ाइन किया गया है।


उदाहरण:

1

List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .collect(Collectors.toList());

हम उपयोग करके नेस्टेड सूची से बच सकते हैं flatMap:

List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .flatMap(i -> i.stream())
  .collect(Collectors.toList());

2

Optional<Optional<String>> result = Optional.of(42)
      .map(id -> findById(id));

Optional<String> result = Optional.of(42)
      .flatMap(id -> findById(id));

कहाँ पे:

private Optional<String> findById(Integer id)

क्षमा करें, लेकिन बिंदु 1 से दूसरा स्निपेट इसके बजाय संकलन नहीं कर रहा है List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());। यह होना चाहिएStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
arthur

@arthur मुझे लगता है कि मैंने यहाँ Vavr की स्ट्रीम और सूची का उपयोग किया है - लेकिन मैं मानता हूँ कि यह थोड़ा भ्रमित हो सकता है - मैं इसे मानक जावा में बदल दूंगा
Grzegorz Piwowarek


22

वैकल्पिक पर ओरेकल का लेख मानचित्र और फ़्लैटमैप के बीच इस अंतर पर प्रकाश डालता है:

String version = computer.map(Computer::getSoundcard)
                  .map(Soundcard::getUSB)
                  .map(USB::getVersion)
                  .orElse("UNKNOWN");

दुर्भाग्य से, यह कोड संकलित नहीं करता है। क्यों? परिवर्तनशील कंप्यूटर प्रकार का है Optional<Computer>, इसलिए मानचित्र विधि को कॉल करना पूरी तरह से सही है। हालाँकि, getSoundcard () वैकल्पिक का एक ऑब्जेक्ट लौटाता है। इसका मतलब है कि मानचित्र संचालन का परिणाम प्रकार का एक उद्देश्य है Optional<Optional<Soundcard>>। नतीजतन, getUSB () के लिए कॉल अमान्य है क्योंकि सबसे बाहरी वैकल्पिक में इसका मूल्य एक अन्य वैकल्पिक है, जो निश्चित रूप से getUSB () विधि का समर्थन नहीं करता है।

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

वैकल्पिक भी एक फ्लैटपाइप विधि का समर्थन करता है। इसका उद्देश्य एक वैकल्पिक के मान पर परिवर्तन फ़ंक्शन को लागू करना है (जैसे मानचित्र संचालन करता है) और फिर परिणामी दो-स्तरीय वैकल्पिक को एक ही में समतल करें

इसलिए, हमारे कोड को सही बनाने के लिए, हमें इसे पुन: लिखना होगा, जैसे कि फ्लैटपाइप का उपयोग करना।

String version = computer.flatMap(Computer::getSoundcard)
                   .flatMap(Soundcard::getUSB)
                   .map(USB::getVersion)
                   .orElse("UNKNOWN");

पहला फ्लैटपाइप सुनिश्चित करता है कि एक के Optional<Soundcard>बजाय एक लौटा दिया गया है Optional<Optional<Soundcard>>, और दूसरा फ्लैटपाइप एक ही उद्देश्य को प्राप्त करता है Optional<USB>। ध्यान दें कि तीसरी कॉल के लिए केवल एक नक्शा () होने की आवश्यकता है क्योंकि getVersion () एक वैकल्पिक वस्तु के बजाय एक स्ट्रिंग देता है।

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html


1
सवाल Stream.map और Stream.flatMap के बारे में था और Optional.map के बारे में नहीं था। Optional.flatMap
djames 16:13

4
लेकिन इसने मुझे वैकल्पिक और फ्लैटमैप के साथ मेरी समस्याओं को समझने में बहुत मदद की, बहुत बहुत धन्यवाद!
Loïc

2
@djames, यह पूरी तरह से मान्य उत्तर है, इसे पैराग्राफ से शुरू करें "धाराओं के साथ, फ्लैटपाइप विधि एक फ़ंक्शन को तर्क के रूप में लेती है ..." :)
skwisgaar

मुझे लगता है कि यह यहां के कुछ अन्य उत्तरों के लिए बहुत उपयोगी है।
Mz A

यदि साउंडकार्ड शून्य है, तो फ्लैटपाइप () संस्करण भी नलपॉइंटेरैसेप्शन फेंकता है। तो जहां वैकल्पिक का वादा किया लाभ है?
एकाटेरिना

16

मुझे यकीन नहीं है कि मैं इसका उत्तर देने वाला हूं, लेकिन हर बार जब मैं किसी ऐसे व्यक्ति का सामना करता हूं जो इसे नहीं समझता है, तो मैं उसी उदाहरण का उपयोग करता हूं।

कल्पना कीजिए कि आपके पास एक सेब है। A mapउस सेब को apple-juiceउदाहरण के लिए या वन-टू-वन मैपिंग में बदल रहा है।

वही सेब लें और उसमें से केवल बीज प्राप्त करें, यही वह flatMapहै, या एक से कई , एक सेब इनपुट के रूप में, कई बीज उत्पादन के लिए।


4
यह एक दिलचस्प उदाहरण है :)
cassiomolin

के लिए flatMapमामला है, आप पहली बार एक सेब से बीज अलग बैग में, प्रति सेब एक बैग, एकत्रित करते हैं इससे पहले कि आप एक भी बैग में बैग के सभी डालना?
डेरेक महार

@DerekMahar यह जावा -10 से पहले एक बैग में एक गरीब हुआ करता flatmapथा , जिसका अर्थ वास्तव में आलसी नहीं था, लेकिन जावा -10 के बाद से यह आलसी है
यूजीन

@ यूजीन कृपया थोड़ा और आलसी अवधारणा की व्याख्या करें, जिसे आप स्पष्ट करने की कोशिश नहीं कर रहे हैं। मुझे समझ में आ रहा है कि derkerMahar ने टिप्पणी में क्या समझाया है कि यह java10 से पहले क्या हो रहा है?
JAVA

@JAVA बस खोज flatMap + lazy, मुझे यकीन है कि वहाँ कुछ जवाब होगा।
यूजीन

16

नक्शा () और फ्लैट मैप ()

  1. map()

बस एक फंक्शन लेता है एक लैम्ब्डा परम जहां टी तत्व है और आर टी का उपयोग करके बनाया गया वापसी तत्व है। अंत में हमारे पास टाइप आर की वस्तुओं के साथ एक स्ट्रीम होगा। एक सरल उदाहरण हो सकता है:

Stream
  .of(1,2,3,4,5)
  .map(myInt -> "preFix_"+myInt)
  .forEach(System.out::println);

यह केवल प्रकार 1 से 5 के तत्वों को लेता है Integer, प्रत्येक तत्व का उपयोग प्रकार Stringके मूल्य से एक नया तत्व बनाने के लिए करता है "prefix_"+integer_valueऔर इसे प्रिंट करता है।

  1. flatMap()

यह जानना उपयोगी है कि फ्लैटपाइप () एक फ़ंक्शन F<T, R>कहाँ ले जाता है

  • टी एक प्रकार है जिसमें से / के साथ एक स्ट्रीम बनाया जा सकता है । यह एक सूची (T.stream) (), एक सरणी (Arrays.stream (someArray)), आदि .. कुछ भी हो सकता है जिसमें से एक स्ट्रीम / या फ़ॉर्म के साथ हो सकता है। प्रत्येक देव के नीचे उदाहरण में कई भाषाएं हैं, इसलिए देव। भाषाएँ एक सूची है और एक लैम्बडा पैरामीटर का उपयोग करेगी।

  • R परिणामी स्ट्रीम है जिसे T के उपयोग से बनाया जाएगा। यह जानते हुए कि हमारे पास T के कई उदाहरण हैं, हम स्वाभाविक रूप से R से कई स्ट्रीम होंगे। टाइप R से इन सभी स्ट्रीम को अब टाइप R से एक सिंगल 'फ्लैट' स्ट्रीम में जोड़ा जाएगा।

उदाहरण

बाछिरी तौफीक के उदाहरण यहाँ इसका उत्तर सरल और समझने में आसान है। स्पष्टता के लिए, बस हम डेवलपर्स की एक टीम है:

dev_team = {dev_1,dev_2,dev_3}

कई भाषाओं को जानने वाले प्रत्येक डेवलपर के साथ:

dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_2 = {lang_e,lang_f}

प्रत्येक देव की भाषाएँ पाने के लिए dev_team पर Stream.map () लागू करना :

dev_team.map(dev -> dev.getLanguages())

आप इस संरचना दे देंगे:

{ 
  {lang_a,lang_b,lang_c},
  {lang_d},
  {lang_e,lang_f}
}

जो मूल रूप से एक है List<List<Languages>> /Object[Languages[]]। न तो बहुत सुंदर, न ही जावा-जैसा !!

इसके साथ Stream.flatMap()आप चीजों को 'समतल' कर सकते हैं क्योंकि यह उपरोक्त संरचना को लेता है
और इसे बदल देता है {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}, जिसे मूल रूप से उपयोग किया जा सकता है List<Languages>/Language[]/etc...

इसलिए अंत में, आपका कोड इस तरह से अधिक समझ में आएगा:

dev_team
   .stream()    /* {dev_1,dev_2,dev_3} */
   .map(dev -> dev.getLanguages()) /* {{lang_a,...,lang_c},{lang_d}{lang_e,lang_f}}} */
   .flatMap(languages ->  languages.stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
   .doWhateverWithYourNewStreamHere();

या केवल:

dev_team
       .stream()    /* {dev_1,dev_2,dev_3} */
       .flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
       .doWhateverWithYourNewStreamHere();

मानचित्र का उपयोग कब करें () और फ्लैटपाइप का उपयोग करें () :

  • का प्रयोग करें map()जब अपनी स्ट्रीम से प्रकार टी के प्रत्येक तत्व मैप किया जा माना जाता है / एक में तब्दील हो एक प्रकार आर के तत्व परिणाम प्रकार की मैपिंग है (1 प्रारंभिक तत्व -> 1 अंत तत्व) और प्रकार आर के तत्वों की नई धारा वापस आ गया है।

  • का प्रयोग करें flatMap()अपनी स्ट्रीम से प्रकार टी के प्रत्येक तत्व मैप किया गया / एक में तब्दील हो जाता है जब संग्रह एक प्रकार की मैपिंग के प्रकार आर परिणाम तत्वों की है (1 प्रारंभिक तत्व -> n अंत तत्व) । इन संग्रहों को फिर से टाइप आर के तत्वों की एक नई धारा में विलय (या चपटा ) किया जाता है। यह नेस्टेड लूप का प्रतिनिधित्व करने के लिए उदाहरण के लिए उपयोगी है ।

पूर्व जावा 8:

List<Foo> myFoos = new ArrayList<Foo>();
    for(Foo foo: myFoos){
        for(Bar bar:  foo.getMyBars()){
            System.out.println(bar.getMyName());
        }
    }

पोस्ट 8 जावा

myFoos
    .stream()
    .flatMap(foo -> foo.getMyBars().stream())
    .forEach(bar -> System.out.println(bar.getMyName()));

11

मानचित्र: - यह विधि एक फ़ंक्शन को एक तर्क के रूप में लेती है और धारा के सभी तत्वों को पारित फ़ंक्शन लागू करके उत्पन्न परिणामों से मिलकर एक नई धारा लौटाती है।

आइए कल्पना करें, मेरे पास पूर्णांक मानों (1,2,3,4,5) और एक फ़ंक्शन इंटरफ़ेस की सूची है, जिसका तर्क पारित पूर्णांक का वर्ग है। (ई -> ई * ई)।

List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> newList = intList.stream().map( e -> e * e ).collect(Collectors.toList());

System.out.println(newList);

उत्पादन: -

[1, 4, 9, 16, 25]

जैसा कि आप देख सकते हैं, एक आउटपुट एक नई स्ट्रीम है जिसका मान इनपुट स्ट्रीम के मानों का वर्ग है।

[1, 2, 3, 4, 5] -> apply e -> e * e -> [ 1*1, 2*2, 3*3, 4*4, 5*5 ] -> [1, 4, 9, 16, 25 ]

http://codedestine.com/java-8-stream-map-method/

FlatMap: - यह विधि एक फ़ंक्शन के रूप में एक तर्क लेती है, यह फ़ंक्शन एक पैरामीटर T को इनपुट तर्क के रूप में स्वीकार करता है और पैरामीटर R के एक स्ट्रीम को रिटर्न मान के रूप में लौटाता है। जब यह फ़ंक्शन इस स्ट्रीम के प्रत्येक तत्व पर लागू होता है, तो यह नए मानों की एक स्ट्रीम तैयार करता है। प्रत्येक तत्व द्वारा उत्पन्न इन नई धाराओं के सभी तत्वों को फिर एक नई धारा में कॉपी किया जाता है, जो इस पद्धति का एक वापसी मूल्य होगा।

चलो छवि, मेरे पास छात्र वस्तुओं की एक सूची है, जहां प्रत्येक छात्र कई विषयों के लिए विकल्प चुन सकता है।

List<Student> studentList = new ArrayList<Student>();

  studentList.add(new Student("Robert","5st grade", Arrays.asList(new String[]{"history","math","geography"})));
  studentList.add(new Student("Martin","8st grade", Arrays.asList(new String[]{"economics","biology"})));
  studentList.add(new Student("Robert","9st grade", Arrays.asList(new String[]{"science","math"})));

  Set<Student> courses = studentList.stream().flatMap( e -> e.getCourse().stream()).collect(Collectors.toSet());

  System.out.println(courses);

उत्पादन: -

[economics, biology, geography, science, history, math]

जैसा कि आप देख सकते हैं, एक आउटपुट एक नई स्ट्रीम है जिसके मान इनपुट स्ट्रीम के प्रत्येक तत्व द्वारा स्ट्रीम के सभी तत्वों का एक संग्रह है।

[S1, S2, S3] -> [{"इतिहास", "गणित", "भूगोल"}, {"अर्थशास्त्र", "जीव विज्ञान"}, {"विज्ञान", "गणित"}] -> अद्वितीय विषय लें - > [अर्थशास्त्र, जीव विज्ञान, भूगोल, विज्ञान, इतिहास, गणित]

http://codedestine.com/java-8-stream-flatmap-method/


यदि आप केवल डॉक लिंक को प्रमाणित करने के बजाय कोड प्रदान करते हैं, तो इससे फर्क पड़ सकता है
चार्ल्स-एंटोनी

11

.map के लिए है > बी - एक मानचित्रण

Stream.of("dog", "cat")              // stream of 2 Strings
    .map(s -> s.length())            // stream of 2 Integers: [3, 3]

यह किसी भी आइटम Aको किसी भी आइटम में कनवर्ट करता है Bजावाडोक


.flatMap के लिए है एक -> स्ट्रीम <B> concatinating

Stream.of("dog", "cat")             // stream of 2 Strings
    .flatMapToInt(s -> s.chars())   // stream of 6 ints:      [d, o, g, c, a, t]

यह - 1 किसी भी आइटम Aको धर्मान्तरित करता है Stream< B>, फिर - 2 सभी धाराओं को एक (समतल) धारा में परिवर्तित करता है। जावाडोक


नोट 1: हालाँकि उत्तरार्द्ध उदाहरण वस्तुओं की एक धारा (स्ट्रीम) के बजाय प्राइमेटिव (IntStream) की एक धारा में फ़्लैट करता है, फिर भी यह विचार को दिखाता है .flatMap

नोट 2: नाम के बावजूद, String.chars () विधि रिटर्न ints। तो वास्तविक संग्रह होगा: कोड [100, 111, 103, 99, 97, 116] कहां 100है 'd', 111का कोड है'o' आदि । फिर, उदाहरण के लिए, इसे [डी, ओ, जी, सी, ए, टी] के रूप में प्रस्तुत किया गया है।


3
सबसे बढ़िया उत्तर। उदाहरण के साथ सीधे मुद्दे पर
गेब्रियलबी

1

सरल उत्तर।

mapआपरेशन एक उत्पादन कर सकते हैं Streamकी Stream.EXStream<Stream<Integer>>

flatMapऑपरेशन केवल Streamकुछ का उत्पादन होगा । पूर्वStream<Integer>


0

साथ ही अगर आप परिचित हैं तो अच्छा सादृश्य C # के साथ हो सकता है। मूल रूप Selectसे जावा mapऔर सी # SelectManyजावा के समान # flatMap। संग्रह के लिए कोटलिन पर भी यही लागू होता है।


0

यह शुरुआती लोगों के लिए बहुत भ्रामक है। मूल अंतर mapसूची में प्रत्येक प्रविष्टि के लिए एक आइटम का उत्सर्जन करता है और flatMapमूल रूप से एक map+ flattenऑपरेशन है। अधिक स्पष्ट होने के लिए, जब आप एक से अधिक मान की आवश्यकता हो, तो फ़्लैटबॉल का उपयोग करें, उदाहरण के लिए, जब आप सरणियों को वापस करने के लिए एक लूप की उम्मीद कर रहे हों, तो फ़्लैटपाइप इस मामले में वास्तव में सहायक होगा।

मैंने इस बारे में एक ब्लॉग लिखा है, आप इसे यहाँ देख सकते हैं



0

ऑपरेशन को स्ट्रीम करें flatMapऔर mapइनपुट के रूप में एक फ़ंक्शन को स्वीकार करें।

flatMapउम्मीद करता है कि धारा के प्रत्येक तत्व के लिए एक नई धारा वापस आ जाएगी और एक धारा वापस आती है जो प्रत्येक तत्व के लिए फ़ंक्शन द्वारा दिए गए धाराओं के सभी तत्वों को जोड़ती है। दूसरे शब्दों में, flatMapस्रोत से प्रत्येक तत्व के लिए, फ़ंक्शन द्वारा कई तत्व बनाए जाएंगे। http://www.zoftino.com/java-stream-examples#flatmap-operation

mapएक परिवर्तित मान वापस करने के लिए फ़ंक्शन की अपेक्षा करता है और परिवर्तित तत्वों से युक्त एक नई धारा लौटाता है। दूसरे शब्दों में, mapस्रोत से प्रत्येक तत्व के लिए, फ़ंक्शन द्वारा एक परिवर्तित तत्व बनाया जाएगा। http://www.zoftino.com/java-stream-examples#map-operation


0

flatMap()धाराओं के आंशिक आलसी मूल्यांकन का भी लाभ उठाता है। यह मुट्ठी धारा को पढ़ेगा और केवल जब आवश्यक होगा, अगली धारा में जाएगा। व्यवहार के बारे में यहाँ विस्तार से बताया गया है: क्या फ्लैटपूल आलसी होने की गारंटी है?


0

यदि आप map()एक चलना (एक स्तर forलूप) के रूप में सोचते हैं , flatmap()तो दो-स्तरीय चलना (एक नेस्टेड forलूप की तरह ) है। (प्रत्येक पुनरावृत्त तत्व दर्ज करें foo, और foo.getBarList()उस barListपुन: पुनरावृति करें)


map(): एक स्ट्रीम लें, हर एलिमेंट के लिए कुछ करें, हर प्रोसेस का सिंगल रिजल्ट इकट्ठा करें, दूसरी स्ट्रीम को आउटपुट करें। "कुछ कार्य करें" की परिभाषा निहित है। यदि किसी भी तत्व का जुलूस निकलता है null, तो nullइसका उपयोग अंतिम धारा की रचना के लिए किया जाता है। तो, परिणामी स्ट्रीम में तत्वों की संख्या इनपुट स्ट्रीम की संख्या के बराबर होगी।

flatmap(): तत्वों / धाराओं और एक फ़ंक्शन (स्पष्ट परिभाषा) की एक धारा लें, फ़ंक्शन को प्रत्येक स्ट्रीम के प्रत्येक तत्व पर लागू करें, और सभी मध्यवर्ती परिणामी स्ट्रीम को एक बड़ी स्ट्रीम ("सपाट") इकट्ठा करें। यदि किसी तत्व का जुलूस निकलता है null, तो खाली धारा "समतल" के अंतिम चरण में प्रदान की जाती है। परिणामस्वरूप स्ट्रीम में तत्वों की संख्या, सभी इनपुट में सभी भाग लेने वाले तत्वों की कुल है, यदि इनपुट कई धाराएं हैं।

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