Java 8使用流和收集器映射到集合的子列表条目

t77*_*777 5 java java-8 java-stream collectors

我有一个Person对象的集合:

public class Person {

  String name;

  ChildrenListHolder childrenListHolder;
}

public class ChildrenListHolder {
   List<Children> children;
}

public class Children {
   String childrensName;
}
Run Code Online (Sandbox Code Playgroud)

(实体结构由第三方提供.)

现在,我需要一个Map<String,List<Person>>childrensName - > person-list

例如(简化):

Person father: {name: "John", childrensListHolder -> {"Lisa", "Jimmy"}}
Person mother: {name: "Clara", childrensListHolder -> {"Lisa", "Paul"}}
Person george: {name: "George", childrensListHold -> "Paul"}}
Run Code Online (Sandbox Code Playgroud)

我需要的地图是

Map<String, List<Person>> map: {"Lisa"  -> {father, mother},
                                "Jimmy" -> {father},
                                "Paul"  -> {mother, george}}
Run Code Online (Sandbox Code Playgroud)

我可以用一堆for和if来做到这一点.但是我如何使用流和收集器来做到这一点.我尝试了很多方法,但是我无法得到预期的结果.TIA.

Tun*_*aki 9

给定a List<Person> persons,您可以拥有以下内容

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())
           ));
Run Code Online (Sandbox Code Playgroud)

这正在为人们创造一条流.然后每个人通过一个元组来平面映射,该元组持有孩子和每个孩子的人.最后,我们按子名称分组并将所有人员收集到一个列表中.

假设有适当的构造函数的示例代码:

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);
}
Run Code Online (Sandbox Code Playgroud)


Ale*_* C. 5

我可以用一堆for和if来做到这一点.

我知道你要求一个流/收集器解决方案,但无论如何嵌套for循环使用Map#computeIfAbsent工作也很好:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是使用forEach集合中引入的新方法编写的:

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

当然,它不是单线程,也不像Tunaki的解决方案(+1)那样易于并行化,但是你也不需要"束"来实现它(并且你也避免创建临时的映射条目实例).

  • 是的,这也是一个很好的解决方案.优点是你不需要有一个元组来抱孩子和那个人. (2认同)