对具有多个条件的对象列表进行分组

Group a list of objects with multiple conditions

public class Student {
    String name;
    int age;
}

我有一个 Student 对象的列表,我需要根据特定逻辑对它们进行分组:

  1. 将所有名字以“A”开头的学生分组
  2. 将所有姓名以“P”开头的学生分组
  3. 将年龄大于或等于 30 岁的所有学生分组

到目前为止我做了什么:

List<Student> students = List.of(
     new Student("Alex", 31), 
     new Student("Peter", 33), 
     new Student("Antony", 32),
     new Student("Pope", 40), 
     new Student("Michel", 30));

Function<Student, String> checkFunction = e -> {
    if (e.getName().startsWith("A")) {
        return "A-List";
    } else if (e.getName().startsWith("P")) {
        return "P-List";
    } else if (e.getAge() >= 30) {
        return "30's-List";
    } else {
        return "Exception-List";
    }
};

Map<String, List<Student>> result = students.stream().collect(Collectors.groupingBy(checkFunction));

for (var entry : result.entrySet()) {
    System.out.println(entry.getKey() + "---");
    for (Student std : entry.getValue()) {
        System.out.println(std.getName());
    }
}

输出

A-List---
Alex
Antony
P-List---
Peter
Pope
30's-List---
Michel

我理解我所遵循的这个逻辑是错误的,这就是为什么 30 岁的列表没有正确填充的原因。 groupingBy()真的可以吗?

这可以像 中那样处理,但您必须调整 checkFunction 以实际 return 每个 Student 的组。

private Stream<String> mapToGroups(Student e) {
    Builder<String> builder = Stream.builder();
    boolean isException = false;
    if (e.getName().startsWith("A")) {
        builder.add("A-List");
    } else if (e.getName().startsWith("P")) {
        builder.add("P-List");
    } else {
        isException = true;
    }
    if (e.getAge() >= 30) {
        builder.add("30's-List");
    } else if (isException) {
        builder.add("Exception-List");
    }
    return builder.build();
}

但是,如果我们要在 flatMap() 调用中使用此函数,我们将在该过程中丢失 Student。所以我们真正想要的是这个方法 return String<Map.Entry<String, Student>> 以便我们以后可以使用用于分组的键和用于收集组的值:

private Stream<Entry<String, Student>> mapToGroupEntries(Student e) {
    Builder<Entry<String, Student>> builder = Stream.builder();
    boolean isException = false;
    if (e.getName().startsWith("A")) {
        builder.add(new SimpleEntry<>("A-List", e));
    } else if (e.getName().startsWith("P")) {
        builder.add(new SimpleEntry<>("P-List", e));
    } else {
        isException = true;
    }
    if (e.getAge() >= 30) {
        builder.add(new SimpleEntry<>("30's-List", e));
    } else if (isException) {
        builder.add(new SimpleEntry<>("Exception-List", e));
    }
    return builder.build();
}

我们现在可以将此函数用作 flatMap() 调用的一部分,将我们的 Stream<Student> 转换为 Stream<Entry<String, Student>>,然后将它们分组:

Map<String, List<Student>> result = students.stream()
        .flatMap(s -> mapToGroupEntries(s))
        .collect(Collectors.groupingBy(Entry::getKey,
                Collectors.mapping(Entry::getValue, Collectors.toList())));