2015-07-18 35 views
13

Jestem nowy w wyrażaniu lambda i próbowałem go użyć w moim zadaniu.Scalanie strumieni map za pomocą Java 8 Lambda Expression

Problem:

Mam dwie mapy m1 i m2 typu Map<Integer, String>, który ma zostać włączony do jednej mapie Map<Integer, List<String>>, gdzie wartości samych kluczy w obu mapach są gromadzone w formie listy i umieścić do nowej mapy.

Rozwiązanie opiera się na tym, co ja zbadałem:

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; 
           })); 

Ale to rozwiązanie przewiduje źródło List być Map<Integer, List<String>> jako funkcja scalania w toMap spodziewa argumenty i wynik się być tego samego typu.

Nie chcę zmieniać kolekcji źródeł. Podaj swoje dane wejściowe, aby to osiągnąć, używając wyrażenia lambda.

Odpowiedz

11

nie mogę przetestować go teraz, ale myślę, że wszystko, co potrzebne, aby zmienić mapowanie wartości od wejścia :: getValue do listy, która zawiera tę wartość:

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: pomysł był poprawny. Składnia nie była. Obecna składnia działa, choć trochę brzydka. Musi być krótszy sposób, aby to napisać.

Można również zastąpić e -> {..} za pomocą e -> new ArrayList<String>(Arrays.asList(new String[]{e.getValue()})).

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

EDIT:

Albo można zrobić z 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

Nie, to nie działa na mnie – viktor

+2

@viktor Próbowałaś mój oryginalny odpowiedź (która nie przeszła kompilację) lub moją zaktualizowaną odpowiedź? – Eran

+0

Dzięki Eran, to działa. Dla mojego zrozumienia - modyfikujemy program odwzorowujący wartości w celu utworzenia listy i korzystania z tej samej funkcji scalania scalania list. Dobrze? – viktor

17

To zadanie dla groupingBy kolektora:

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

Dzięki Misha, to również działa. Zastanawiasz się, jaka jest skuteczność, co jest lepsze flatMap lub concat? – viktor

+2

@viktor, tutaj nie powinno być prawdziwej różnicy. Równolegle strumienie lub zbieranie do tablicy 'concat' może działać lepiej, ponieważ poprawnie oblicza całkowity rozmiar strumienia. –

+2

@viktor Oprócz tego, co powiedział @TagirValeev, uważam, że 'concat' ma przewagę, jeśli uruchomisz to jako równoległe, ponieważ' concat' może rozdzielić się między dwa strumienie składowe, podczas gdy 'flatMap' będzie co najwyżej używać 2 wątków w ramach obecnej implementacji . Nie mogę teraz spojrzeć na źródło biblioteki strumieni, więc mogę się mylić. Dla większości praktycznych (tj. Sekwencyjnych) zastosowań dwa są równoważne, a "flatMap" jest bardziej zwięzły, więc to właśnie użyłem. – Misha

3

rozwiązanie Miszy jest najlepiej, jeśli chcesz czystej Java-8 olution. Jeśli nie masz nic przeciwko korzystaniu z bibliotek innych firm, będzie to trochę krótsze, używając mojego StreamEx.

Map<Integer, List<String>> map = StreamEx.of(m1, m2) 
     .flatMapToEntry(Function.identity()) 
     .grouping(); 

Wewnętrznie jest taki sam jak w rozwiązaniu Miszy, tylko cukier syntaktyczny.

3

To wydaje się być świetną okazją do skorzystania z Guva Multimap.

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

A jeśli naprawdę potrzebujemy Map<Integer, List<String>>:

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