Reputation: 3329
I have a collection of Person
objects:.
public class Person {
String name;
ChildrenListHolder childrenListHolder;
}
public class ChildrenListHolder {
List<Children> children;
}
public class Children {
String childrensName;
}
(The entity structure is given by third party.)
Now, I need a Map<String,List<Person>>
childrensName -> person-list
For example (simplified):
Person father: {name: "John", childrensListHolder -> {"Lisa", "Jimmy"}}
Person mother: {name: "Clara", childrensListHolder -> {"Lisa", "Paul"}}
Person george: {name: "George", childrensListHold -> "Paul"}}
The map, I need is
Map<String, List<Person>> map: {"Lisa" -> {father, mother},
"Jimmy" -> {father},
"Paul" -> {mother, george}}
I can do that with a bunch of for's and if's. But how can I do this using streams and collectors. I have tried many approaches, but I cannot get the expected result. TIA.
Upvotes: 7
Views: 14833
Reputation: 93882
I can do that with a bunch of for's and if's.
I know that you asked for a stream/collectors solution, but in any case a nested for loop using Map#computeIfAbsent
works fine too:
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);
}
}
and this written using the new forEach
method introduced on collections:
Map<String, List<Person>> map = new HashMap<>();
persons.forEach(p -> p.childrenListHolder.children.forEach(c -> map.computeIfAbsent(c.childrensName, k -> new ArrayList<>()).add(p)));
Of course it's not a one-liner nor easy parallelizable like in Tunaki's solution (+1), but you don't need a "bunch" of if's to achieve that too (and you avoid also the creation of temporary map entries instances).
Upvotes: 5
Reputation: 137269
Given a List<Person> persons
, you can have the following
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())
));
This is creating a Stream over the persons. Then each person is flat mapped by a tuple holding the child and the person for each child. Finally, we group by the child name and collect all the persons into a list.
Sample code assuming there are appropriate constructors:
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);
}
Upvotes: 9