使用自定义收集器进行Java 8分组?

时间:2015-10-26 12:08:19

标签: java lambda java-8 java-stream collectors

我有以下课程。

class Person {

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        return birthday.until(IsoChronology.INSTANCE.dateNow()).getYears();
    }

    public String getName() {
        return name;
    }
}

我希望能够按年龄分组,然后收集人名列表而不是Person对象本身;所有这一切都在一个很好的lamba表达式中。

为了简化所有这些,我将链接当前的解决方案,该解决方案按年龄存储分组结果,然后迭代它以收集名称。

ArrayList<OtherPerson> members = new ArrayList<>();

members.add(new OtherPerson("Fred", IsoChronology.INSTANCE.date(1980, 6, 20), OtherPerson.Sex.MALE, "fred@example.com"));
members.add(new OtherPerson("Jane", IsoChronology.INSTANCE.date(1990, 7, 15), OtherPerson.Sex.FEMALE, "jane@example.com"));
members.add(new OtherPerson("Mark", IsoChronology.INSTANCE.date(1990, 7, 15), OtherPerson.Sex.MALE, "mark@example.com"));
members.add(new OtherPerson("George", IsoChronology.INSTANCE.date(1991, 8, 13), OtherPerson.Sex.MALE, "george@example.com"));
members.add(new OtherPerson("Bob", IsoChronology.INSTANCE.date(2000, 9, 12), OtherPerson.Sex.MALE, "bob@example.com"));

Map<Integer, List<Person>> collect = members.stream().collect(groupingBy(Person::getAge));

Map<Integer, List<String>> result = new HashMap<>();

collect.keySet().forEach(key -> {
            result.put(key, collect.get(key).stream().map(Person::getName).collect(toList()));
});

Current solution

不理想,为了学习,我希望有一个更优雅和更好的解决方案。

3 个答案:

答案 0 :(得分:13)

使用Collectors.groupingBy对流进行分组时,您可以使用自定义Collector为值指定缩减操作。在这里,我们需要使用Collectors.mapping,它接受​​一个函数(映射是什么)和一个收集器(如何收集映射值)。在这种情况下,映射是Person::getName,即返回Person名称的方法引用,我们将其收集到List

Map<Integer, List<String>> collect = 
    members.stream()
           .collect(Collectors.groupingBy(
               Person::getAge,
               Collectors.mapping(Person::getName, Collectors.toList()))
           );

答案 1 :(得分:1)

您还可以使用Collectors.toMap并提供键,值和合并功能的映射(如果有)。

Map<Integer, String> ageNameMap = 
    members.stream()
            .collect(Collectors.toMap(
              person -> person.getAge(), 
              person -> person.getName(), (pName1, pName2) -> pName1+"|"+pName2)
    );

答案 2 :(得分:0)

您可以使用mapping CollectorPerson列表映射到人名列表:

Map<Integer, List<String>> collect = 
    members.stream()
           .collect(Collectors.groupingBy(Person::getAge,
                                          Collectors.mapping(Person::getName, Collectors.toList())));