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

我有以下课程。

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 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> collect = members.stream().collect(groupingBy(Person::getAge)); Map<Integer, List> result = new HashMap(); collect.keySet().forEach(key -> { result.put(key, collect.get(key).stream().map(Person::getName).collect(toList())); }); 

当前解决方案

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

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

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

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

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

您可以使用mapping CollectorPerson列表mapping到人员名称列表:

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