2015-07-18 1 views
13

Ich bin neu in Lambda-Ausdruck und versuchte, es in meiner Aufgabe zu verwenden.Map-Streams mit Java 8 Lambda-Ausdruck verschmelzen

Das Problem:

Ich habe zwei Karten m1 und m2 vom Typ Map<Integer, String>, die Map<Integer, List<String>> in einer einzigen Karte verschmolzen werden muss, in denen Werte von gleichen Tasten in den beiden Karten in einer Liste gesammelt und setzen in eine neue Karte.

Lösung basiert auf, was ich untersucht:

Map<Integer, List<String>> collated = 
       Stream.concat(m1.entrySet().stream(), m2.entrySet().stream()).collect(
         Collectors.toMap(Entry::getKey, 
           Entry::getValue, (a, b) -> { 
            List<String> merged = new ArrayList<String>(a); 
            merged.addAll(b); 
            return merged; 
           })); 

aber erwartet, dass diese Lösung List Quelle Map<Integer, List<String>> als die Merge-Funktion in toMap erwartet Operanden und Ergebnis werden vom gleichen Typ zu sein.

Ich möchte die Quellensammlung nicht ändern. Bitte geben Sie Ihre Eingaben an, um dies mit Lambda-Ausdruck zu erreichen.

Antwort

11

Ich kann es nicht testen, gerade jetzt, aber ich denke, alles, was Sie es brauchen die Zuordnung des Wertes aus Eintrag :: getValue auf eine Liste zu ändern, dass dieser Wert enthält:

Map<Integer, List<String>> collated = 
    Stream.concat(m1.entrySet().stream(), m2.entrySet().stream()) 
      .collect(Collectors.toMap(Entry::getKey, 
            e -> { 
              List<String> v = new ArrayList<String>(); 
              v.add(e.getValue()); 
              return v; 
             }, 
            (a, b) -> { 
             List<String> merged = new ArrayList<String>(a); 
             merged.addAll(b); 
             return merged; 
            })); 

EDIT: Die Idee war richtig. Die Syntax war nicht. Aktuelle Syntax funktioniert, obwohl ein bisschen hässlich. Es muss einen kürzeren Weg geben, um es zu schreiben.

Sie können auch e -> {..} durch e -> new ArrayList<String>(Arrays.asList(new String[]{e.getValue()})) ersetzen.

oder mit e -> Stream.of(e.getValue()).collect(Collectors.toList())

EDIT:

Oder Sie können es tun, mit groupingBy:

Map<Integer, List<String>> collated = 
    Stream.concat(m1.entrySet().stream(), m2.entrySet().stream()) 
      .collect(Collectors.groupingBy(Map.Entry::getKey, 
             Collectors.mapping(Map.Entry::getValue, 
                  Collectors.toList()))); 
+0

Nein, es ist nicht für mich arbeiten – viktor

+2

@viktor Haben Sie meine ursprüngliche Antwort versuchen (die nicht Kompilation bestanden hat) oder meine Antwort aktualisiert? – Eran

+0

Danke Eran, das funktioniert. Zu meinem Verständnis - wir modifizieren Wert-Mapper, um List zu erzeugen und die gleiche Merge-Funktion von Merging Listen zu verwenden. Recht? – viktor

17

Das ist eine Aufgabe für groupingBy Sammler:

Stream.of(m1,m2) 
     .flatMap(m->m.entrySet().stream()) 
     .collect(groupingBy(
       Map.Entry::getKey, 
       mapping(Map.Entry::getValue, toList()) 
     )); 
+1

Danke Misha, das funktioniert auch. Wundern Sie sich über die Leistung, was ist besser, FlatMap oder Concat? – viktor

+2

@viktor, hier sollte kein wirklicher Unterschied sein. Bei parallelen Streams oder dem Sammeln zu einem Array kann "concat" eine bessere Leistung erbringen, da es die Gesamtgröße des Streams richtig berechnet. –

+2

@viktor Zusätzlich zu dem, was @TagirValeev gesagt hat, glaube ich, dass 'concat' einen Vorteil hat, wenn Sie dies parallel ausführen, da' concat' sich innerhalb der beiden Teilströme aufteilen kann, während 'flatMap' maximal 2 Threads unter der aktuellen Implementierung verwendet . Ich kann die Quelle der Stream-Bibliothek jetzt nicht ansehen, daher kann ich mich irren. Für die meisten praktischen (d. H. Sequenziellen) Anwendungen sind die beiden äquivalent und "flatMap" ist prägnanter, also habe ich das verwendet. – Misha

3

Mischas Lösung ist die Am besten, wenn Sie reine Java-8 s wollen Lösung. Wenn es Ihnen nichts ausmacht, Bibliotheken von Drittanbietern zu verwenden, wäre es etwas kürzer, wenn Sie meine StreamEx verwenden.

Intern ist es das gleiche wie in Mischas Lösung, nur syntaktischer Zucker.

3

Dies scheint eine gute Gelegenheit zu sein, Guavas Multimap zu verwenden.

ListMultimap<Integer, String> collated = ArrayListMultimap.create(); 
collated.putAll(Multimaps.forMap(m1)); 
collated.putAll(Multimaps.forMap(m2)); 

Und wenn Sie wirklich ein Map<Integer, List<String>> brauchen:

Map<Integer, List<String>> mapCollated = Multimaps.asMap(collated);