有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java流分组方式+聚合

public class Member {
  private String name;
  private String team;
  private List<String> tasks;
  ...
}

输入列表,如:

member1, team1, [task1]
member2, team1, [task2]
member2, team1, [taks3]
member3, team2, [task4]
member3, team2, [task5, task6]

尝试按团队分组并将每个成员的任务聚合在一起,如何编写流?预期结果类似于Map<String, List<Member>>

team1: [
  member1: [task1]
  member2: [task2, task3]
]
team2: [
  member3: [task4, task5, task6]
]

按团队分组很容易。挑战在于如何为每个不同的成员聚合任务,并将结果保持为“Map>

更新:这是我能做的最好的了,但它有两个步骤。有人能把这些合并成一个吗

public class Test {
public static void main(String[] args) {
    Member m1 = new Member("member1", "team1", task("task1"));
    Member m2 = new Member("member2", "team1", task("task2"));
    Member m3 = new Member("member2", "team1", task("task3"));
    Member m4 = new Member("member3", "team2", task("task4"));
    Member m5 = new Member("member3", "team2", task("task5"));

    List<Member> list = Arrays.asList(m1, m2, m3, m4, m5);

    long start = System.currentTimeMillis();

    Map<String, Map<String, Optional<Member>>> result = list.stream()
            .collect(Collectors.groupingBy(Member::getTeam,
                    Collectors.groupingBy(Member::getName, Collectors.reducing(
                            (member1, member2) -> {
                                member1.getTasks().addAll(member2.getTasks());
                                return member1;
                            }
                    ))));
    Map<String, List<Member>> result2 = result.entrySet().stream()
            .collect(Collectors.toMap(entry -> entry.getKey(),
                    entry -> entry.getValue().values().stream()
                            .map(e -> e.get()).collect(Collectors.toList())));

    result2.entrySet().stream().forEach(System.out::println);

    long end = System.currentTimeMillis();
    System.out.println("Total:" + (end - start) + "ms");
}

private static List<String> task(String... tasks) {
    List<String> list = new ArrayList<>();
    Collections.addAll(list, tasks);
    return list;
}

}


共 (3) 个答案

  1. # 1 楼答案

    这可以通过使用Collectors.toMap函数来实现

    在你的例子中:

    Map<String, Member> mapByTeams =
      listOfMembers.stream().collect(Collectors.toMap(Member::getTeam, Function.identity()));
    
  2. # 2 楼答案

    下面的收集器组合应该可以做到这一点

    我相信这是一个很好的例子,说明了在技术上是可行的,但在可读性方面并不是一个好主意

    List<Member> list = ...
    Map<String, Map<String, List<String>>> map = list.stream()
            .collect(Collectors.groupingBy(Member::getTeam, 
                Collectors.groupingBy(Member::getName,
                  Collectors.mapping(Member::getTasks,
                    Collectors.collectingAndThen(Collectors.toList(), 
                        (doubleList) -> doubleList.stream()
                          .flatMap(lst -> lst.stream())
                            .collect(Collectors.toList()))))));
    

    使用以下输入进行测试时(添加了构造函数):

    List<Member> list = Arrays.asList(
            new Member("name1", "team1", Arrays.asList("task1", "task11")),
            new Member("name1", "team1", Arrays.asList("task2", "task22")),
            new Member("name1", "team2", Arrays.asList("task1", "task11")));
    

    生成的地图包含:

    {team1={name1=[task1, task11, task2, task22]}, team2={name1=[task1, task11]}}
    
  3. # 3 楼答案

    您的任务包括两个步骤:按团队分组,然后按成员分组,然后合并他们的任务

    试试这个:

    Map<String, List<Member>> result =
               members.stream()
                      .collect(groupingBy(Member::getTeam,
                               collectingAndThen(groupingBy(Member::getName,
                                            reducing(null, (m1, m2) -> {
                                                if (m1 == null) {
                                                    return m2;
                                                }
                                                List<String> tasks = new ArrayList<>();
                                                tasks.addAll(m1.getTasks());
                                                tasks.addAll(m2.getTasks());
                                                m2.setTasks(tasks);
                                                return m2;
                                            })), map -> new ArrayList<>(map.values()))));