按多个值分组

时间:2017-06-29 14:09:21

标签: java group-by java-stream

例如,我有一个学生列表及其学期和科目。

Subject   Semester   Attendee
---------------------------------
ITB001    1          John
ITB001    1          Bob
ITB001    1          Mickey
ITB001    2          Jenny
ITB001    2          James
MKB114    1          John
MKB114    1          Erica

当我需要使用Stream api的一个值对它们进行分组时,我可以制作类似的内容;

Map<String, List<Student>> studlistGrouped =
    studlist.stream().collect(Collectors.groupingBy(w -> w.getSubject()));

它确实有效。但是,当我想用​​两个或多个值(如Sql句柄)对它们进行分组时,我无法弄清楚我需要做什么。

2 个答案:

答案 0 :(得分:1)

一种方法是按类分组,该类包含您要分组的所有字段。此类必须使用所有这些字段实现hashCodeequals才能按预期工作。

在您的示例中,这将是:

public class SubjectAndSemester {

    private final String subject;

    private final int semester; // an enum sounds better

    public SubjectAndSemester(String subject, int semester) {
        this.subject = subject;
        this.semester = semester;
    }

    // TODO getters and setters, hashCode and equals using both fields
}

然后,您应该在Student类中创建此方法:

public SubjectAndSemester bySubjectAndSemester() {
    return new SubjectAndSemester(subject, semester);
}

现在,您可以按如下方式进行分组:

Map<SubjectAndSemester, List<Student>> studlistGrouped = studlist.stream()
    .collect(Collectors.groupingBy(Student::bySubjectAndSemester));

这会创建一个级别的学生分组。

还有另一个选项,它不要求您使用Tuple或创建要分组的新类。我的意思是你可以通过使用下游groupingBy收藏家来获得 n 级别的学生分组:

Map<String, Map<Integer, List<Student>>> studlistGrouped = studlist.stream()
    .collect(Collectors.groupingBy(Student::getSubject,
        Collectors.groupingBy(Student::getSemester)));

这会创建一个2级分组,因此返回的结构现在是列表映射的映射。如果你可以忍受这种情况,即使是对更多关卡进行分组,你也可以避免创建一个充当地图关键字的类。

答案 1 :(得分:0)

您可以创建一个计算字段,它是两列

的组合
Map<String, List<Student>> studlistGrouped =
    studlist.stream().collect(Collectors.groupingBy(w -> w.getSubject() + "-" w.getAttendee()));

修改
另一个更“合适”的解决方案:Accoridng到this blog post,你需要定义一个可以容纳两列的Tuple类:

class Tuple {
    String subject;
    String attendee;
}
Map<String, List<Student>> studlistGrouped =
    studlist.stream().collect(Collectors.groupingBy(w -> new Tuple(w.getSubject(), w.getAttendee())));