2016-03-28 30 views
5

Mam kolekcję obiektów Person :.Java 8 mapowanie do wpisów na liście podrzędnej kolekcji przy użyciu strumieni i kolektorów

public class Person { 

    String name; 

    ChildrenListHolder childrenListHolder; 
} 

public class ChildrenListHolder { 
    List<Children> children; 
} 

public class Children { 
    String childrensName; 
} 

(Struktura jednostka jest przez stronę trzecią.)

Teraz muszę Map<String,List<Person>> childrensName -> person-listy

Na przykład (uproszczony):

Person father: {name: "John", childrensListHolder -> {"Lisa", "Jimmy"}} 
Person mother: {name: "Clara", childrensListHolder -> {"Lisa", "Paul"}} 
Person george: {name: "George", childrensListHold -> "Paul"}} 

Potrzebna jest mapa

Map<String, List<Person>> map: {"Lisa" -> {father, mother}, 
           "Jimmy" -> {father}, 
           "Paul" -> {mother, george}} 

Mogę to zrobić z grupą forów i jeśli jest. Ale jak mogę to zrobić za pomocą strumieni i kolektorów. Próbowałem wielu podejść, ale nie mogę uzyskać oczekiwanego rezultatu. TIA.

Odpowiedz

7

otrzymał List<Person> persons, można mieć następującą

Map<String,List<Person>> map = 
    persons.stream() 
      .flatMap(p -> p.childrenListHolder.children.stream().map(c -> new AbstractMap.SimpleEntry<>(c, p))) 
      .collect(Collectors.groupingBy(
      e -> e.getKey().childrensName, 
      Collectors.mapping(Map.Entry::getValue, Collectors.toList()) 
      )); 

To tworzy strumieniowo osób. Następnie każda osoba jest płaska odwzorowana przez krotkę trzymającą dziecko i osobę dla każdego dziecka. Na koniec grupujemy według nazwiska dziecka i gromadzimy wszystkie osoby na liście.

Przykładowy kod zakładając istnieją odpowiednie konstruktory:

public static void main(String[] args) { 
    List<Person> persons = Arrays.asList(
     new Person("John", new ChildrenListHolder(Arrays.asList(new Children("Lisa"), new Children("Jimmy")))), 
     new Person("Clara", new ChildrenListHolder(Arrays.asList(new Children("Lisa"), new Children("Paul")))), 
     new Person("George", new ChildrenListHolder(Arrays.asList(new Children("Paul")))) 
    ); 

    Map<String,List<Person>> map = 
     persons.stream() 
       .flatMap(p -> p.childrenListHolder.children.stream().map(c -> new AbstractMap.SimpleEntry<>(c, p))) 
       .collect(Collectors.groupingBy(
       e -> e.getKey().childrensName, 
       Collectors.mapping(Map.Entry::getValue, Collectors.toList()) 
       )); 

    System.out.println(map); 
} 
+0

Dzięki. Niestety to nie działa. Wywołanie flatMap zwraca 'Stream ', więc zbieranie za pomocą 'e.getKey' nie działa. – t777

+1

@ t777 Hmm Właśnie uruchomiłem ten kod w Eclipse Mars.2 i działało dobrze. Z którego IDE korzystasz? – Tunaki

+0

Używam Eclipse Luna 2 (4.4.2) – t777

5

mogę zrobić z grupą za i jeśli tych.

wiem, że poprosił o rozwiązanie stream/kolektory, ale w każdym przypadku zagnieżdżonych pętli przy użyciu Map#computeIfAbsent działa zbyt dobrze:

Map<String, List<Person>> map = new HashMap<>(); 
for(Person p : persons) { 
    for(Children c : p.childrenListHolder.children) { 
     map.computeIfAbsent(c.childrensName, k -> new ArrayList<>()).add(p); 
    } 
} 

i to napisane przy użyciu nowego forEach metoda wprowadzona na zbiorach :

Map<String, List<Person>> map = new HashMap<>(); 
persons.forEach(p -> p.childrenListHolder.children.forEach(c -> map.computeIfAbsent(c.childrensName, k -> new ArrayList<>()).add(p))); 

oczywiście to nie jest jedno-liner, ani łatwe parallelizable jak w roztworze Tunaki'S (+1), ale nie trzeba się "kilka" od tego, czy jest to osiągnąć zbyt (a yo Unikaj także tworzenia tymczasowych instancji wpisów map.

+2

Tak, to również dobre rozwiązanie. Zaletą jest to, że nie musisz mieć krotki do trzymania dzieci i osoby. – Tunaki

+0

Dziękuję. Podoba mi się również twoje rozwiązanie. Chciałbym móc przyjąć dwie odpowiedzi. ;) – t777

Powiązane problemy