2016-06-24 20 views
17

के लिए लॉग इन करें, तो मैं कैसे उपयोग करना चाहता हूं 2 स्ट्रीम कॉल में नीचे दिखाया गया है। मैं कुछ शर्तों के आधार पर संग्रह को 2 नए संग्रहों में विभाजित करना चाहता हूं। आदर्श रूप से मैं इसे 1 में करना चाहता हूं। मैंने धाराओं के .map फ़ंक्शन के लिए उपयोग की जाने वाली स्थितियों को देखा है, लेकिन प्रत्येक के लिए कुछ भी नहीं मिला। मुझे क्या हासिल करने का सबसे अच्छा तरीका क्या है?यदि जावा 8 स्ट्रीम में प्रत्येक

animalMap.entrySet().stream() 
      .filter(pair-> pair.getValue() != null) 
      .forEach(pair-> myMap.put(pair.getKey(), pair.getValue())); 

    animalMap.entrySet().stream() 
      .filter(pair-> pair.getValue() == null) 
      .forEach(pair-> myList.add(pair.getKey())); 
+1

ऐसी स्थिति की तरह लगता है जहां धाराएं वास्तव में आपको कोई पक्ष नहीं दे रही हैं। यह सिर्फ एपीआई के साथ कंट्रोल फ्लो सिंटैक्स को छुपा रहा है जो अजीब हो जाता है और आपका 'फॉरएच' लैम्बडा स्टेटफुल है। – Radiodef

उत्तर

28

बस स्थिति को लैम्ब्डा में ही रखें, उदा।

animalMap.entrySet().stream() 
     .forEach(
       pair -> { 
        if (pair.getValue() != null) { 
         myMap.put(pair.getKey(), pair.getValue()); 
        } else { 
         myList.add(pair.getKey()); 
        } 
       } 
     ); 
बेशक

, इस मान लिया गया है कि दोनों संग्रहों (myMap और myList) घोषित कर दिया और कोड के ऊपर टुकड़ा करने से पहले प्रारंभ कर रहे हैं।


अद्यतन:Map.forEach का उपयोग कर, कोड कम, प्लस और अधिक कुशल और पठनीय बनाता है के रूप में Jorn Vernee कृपया सुझाव:

animalMap.forEach(
      (key, value) -> { 
       if (value != null) { 
        myMap.put(key, value); 
       } else { 
        myList.add(key); 
       } 
      } 
    ); 
+3

इसके बजाय आप '' 'Map.forEach''' का उपयोग कर सकते हैं, यह थोड़ा और संक्षिप्त होगा। –

+1

@ जोर्नवेर्नी, संकेत के लिए आपको बहुत बहुत धन्यवाद। –

+1

आप लैम्ब्डा अभिव्यक्ति में ब्रेसिज़ '{...} 'का भी उपयोग कर सकते हैं, यदि यह एक साधारण टर्नरी से अधिक संभाल सकता है। – dcsohl

3

अंदर add या put के लिए एक कॉल के साथ stream().forEach(..) का उपयोग करके समस्या forEach (इसलिए आप बाहरी myMap या myList उदाहरण को म्यूटेट करते हैं) यह है कि यदि आप समांतर में स्ट्रीम को बदलते हैं तो आप आसानी से समवर्ती मुद्दों में भाग सकते हैं और जिस संग्रह को आप संशोधित कर रहे हैं वह थ्रेड सुरक्षित नहीं है।

एक दृष्टिकोण जो आप ले सकते हैं वह मूल मानचित्र में प्रविष्टियों को पहले विभाजित करना है। एक बार आपके पास हो जाने के बाद, प्रविष्टियों की इसी सूची को पकड़ें और उचित मानचित्र और सूची में उन्हें एकत्र करें।

Map<Boolean, List<Map.Entry<K, V>>> partitions = 
    animalMap.entrySet() 
      .stream() 
      .collect(partitioningBy(e -> e.getValue() == null)); 

Map<K, V> myMap = 
    partitions.get(false) 
       .stream() 
       .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); 

List<K> myList = 
    partitions.get(true) 
       .stream() 
       .map(Map.Entry::getKey) 
       .collect(toList()); 

... या यदि आप एक पास में यह करना चाहते हैं, एक कस्टम कलेक्टर (यह मानते हुए एक Tuple2<E1, E2> वर्ग मौजूद है, आप अपने खुद के बना सकते हैं), उदाहरण के लिए लागू:

public static <K,V> Collector<Map.Entry<K, V>, ?, Tuple2<Map<K, V>, List<K>>> customCollector() { 
    return Collector.of(
      () -> new Tuple2<>(new HashMap<>(), new ArrayList<>()), 
      (pair, entry) -> { 
       if(entry.getValue() == null) { 
        pair._2.add(entry.getKey()); 
       } else { 
        pair._1.put(entry.getKey(), entry.getValue()); 
       } 
      }, 
      (p1, p2) -> { 
       p1._1.putAll(p2._1); 
       p1._2.addAll(p2._2); 
       return p1; 
      }); 
} 
साथ

अपने उपयोग:

Tuple2<Map<K, V>, List<K>> pair = 
    animalMap.entrySet().parallelStream().collect(customCollector()); 

आप कर सकते हैं धुन इसे और अधिक अगर तुम चाहते हो, पैरामीटर के रूप में एक विधेय प्रदान करके उदाहरण के लिए।

3

ज्यादातर मामलों में, जब आप स्ट्रीम पर forEach का उपयोग करके स्वयं को पाते हैं, तो आपको पुनर्विचार करना चाहिए कि क्या आप अपने काम के लिए सही टूल का उपयोग कर रहे हैं या आप इसे सही तरीके से उपयोग कर रहे हैं या नहीं।

आम तौर पर, आपको एक उपयुक्त टर्मिनल ऑपरेशन की तलाश करनी चाहिए जो आप प्राप्त करना चाहते हैं या उपयुक्त कलेक्टर के लिए। अब, Map एस और List एस के उत्पादन के लिए संग्राहक हैं, लेकिन एक अनुमान के आधार पर दो अलग-अलग संग्राहकों के संयोजन के लिए बॉक्स ऑफ कलेक्टर से बाहर नहीं है।

अब, this answer में दो संग्राहक संयोजन के लिए एक संग्राहक शामिल है। इस कलेक्टर का उपयोग करना, आप के रूप में

Pair<Map<KeyType, Animal>, List<KeyType>> pair = animalMap.entrySet().stream() 
    .collect(conditional(entry -> entry.getValue() != null, 
      Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue), 
      Collectors.mapping(Map.Entry::getKey, Collectors.toList()))); 
Map<KeyType,Animal> myMap = pair.a; 
List<KeyType> myList = pair.b; 

कार्य प्राप्त कर सकते हैं लेकिन शायद, आप एक सरल तरीके से इस विशेष कार्य हल कर सकते हैं। आप में से एक परिणाम इनपुट प्रकार से मेल खाता है; यह वही नक्शा है जो प्रविष्टियों को तोड़ देता है जो null पर नक्शा लगाते हैं।यदि आपका मूल नक्शा परिवर्तनशील है और आप इसे बाद में की जरूरत नहीं है, तो आप सिर्फ सूची को इकट्ठा करने और के रूप में वे परस्पर अनन्य हैं मूल नक्शे से इन कुंजियों को दूर कर सकते हैं:

List<KeyType> myList=animalMap.entrySet().stream() 
    .filter(pair -> pair.getValue() == null) 
    .map(Map.Entry::getKey) 
    .collect(Collectors.toList()); 

animalMap.keySet().removeAll(myList); 

ध्यान दें कि आप null लिए मैपिंग को हटा सकते हैं

animalMap.values().removeIf(Objects::isNull); 

या

animalMap.values().removeAll(Collections.singleton(null)); 

: यहां तक ​​कि अन्य कुंजियों की सूची बिना

यदि आप मूल मानचित्र को संशोधित नहीं कर सकते (या नहीं चाहते हैं), कस्टम कलेक्टर के बिना अभी भी एक समाधान है। के रूप में Alexis C.’s answer में संकेत दिया, partitioningBy सही दिशा में जा रहा है, लेकिन आप इसे आसान बनाने में कर सकते हैं:

Map<Boolean,Map<KeyType,Animal>> tmp = animalMap.entrySet().stream() 
    .collect(Collectors.partitioningBy(pair -> pair.getValue() != null, 
       Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); 
Map<KeyType,Animal> myMap = tmp.get(true); 
List<KeyType> myList = new ArrayList<>(tmp.get(false).keySet()); 

लब्बोलुआब यह है, साधारण संग्रह संचालन के बारे में भूल नहीं है, आप के साथ सब कुछ करने की जरूरत नहीं है नया स्ट्रीम एपीआई।

+0

होल्गर, क्या आप सहमत नहीं हैं कि आपका समाधान निश्चित रूप से स्वीकार किए गए एक से कम पठनीय है? –

+2

@ मार्को अल्टेरी: यह वास्तविक प्रश्न पर निर्भर करता है। प्रश्न actu सहयोगी ने * स्ट्रीम एपीआई * के बारे में पूछा, जिसे स्वीकृत उत्तर वास्तव में उत्तर नहीं देता है, अंत में, 'के लिए' केवल 'लूप' के लिए एक वैकल्पिक वाक्यविन्यास है। उदा।, 'Map.forEach (...)' संस्करण समानांतर में नहीं चलाया जा सकता है, 'एंट्रीसेट()। स्ट्रीम()। प्रत्येक (...)' variant समानांतर में चलते समय, भयानक रूप से तोड़ देगा। जब आप स्ट्रीम एपीआई का उपयोग करना चाहते हैं और समझते हैं, इसका सही तरीके से उपयोग कैसे करें, तो आपको एलेक्सिस सी के उत्तर या मेरा साथ जाना होगा। एक बार समझने के बाद, यह आपके लिए अपठनीय नहीं लगेगा ... – Holger

0

मुझे लगता है कि यह जावा 9 में संभव है: यह हालांकि काम करने के लिए

animalMap.entrySet().stream() 
       .forEach(
         pair -> Optional.ofNullable(pair.getValue()) 
           .ifPresentOrElse(v -> myMap.put(pair.getKey(), v), v -> myList.add(pair.getKey()))) 
       ); 

ifPresentOrElse की आवश्यकता है। (मुझे लगता है कि लूप के लिए बेहतर दिखता है।)

संबंधित मुद्दे