流分组后如何聚合分组实体
How to aggregate grouped entities after stream groupingBy
我有一个简单的 class Person
:
class Person {
String firstName;
String lastName;
//getter, setter, constructor, toString
}
还有一个 Persons
的输入列表,例如:
List<Person> myList = List.of(
new Person("Helena", "Graves"),
new Person("Jasmine", "Knight"),
new Person("Phoebe", "Reyes"),
new Person("Aysha", "Graham"),
new Person("Madeleine", "Jenkins"),
new Person("Christina", "Johnson"),
new Person("Melissa", "Carpenter"),
new Person("Marie", "Daniel"),
new Person("Robin", "French"),
new Person("Tamara", "Wyatt"),
new Person("Freya", "Montgomery"),
new Person("Lacey", "Todd"),
new Person("Heather", "Parker"),
new Person("Lauren", "Wright"),
new Person("Annie", "Bradley")
);
现在我需要将上面的列表按此人姓氏的第一个字符进行分组,然后再次对这些组进行分组,以便所有在 A-H
之间开始的姓氏属于一个组,下一组属于那些从 I-N
开始,最后是 O-Z
.
我已经可以按姓氏的第一个字符对列表进行分组:
myList.stream()
.collect(Collectors.groupingBy(p -> String.valueOf(p.getLastName().charAt(0))))
.entrySet()
.forEach(System.out::println);
这给了我:
P=[Person{Heather, Parker}]
B=[Person{Annie, Bradley}]
R=[Person{Phoebe, Reyes}]
C=[Person{Melissa, Carpenter}]
T=[Person{Lacey, Todd}]
D=[Person{Marie, Daniel}]
F=[Person{Robin, French}]
W=[Person{Tamara, Wyatt}, Person{Lauren, Wright}]
G=[Person{Helena, Graves}, Person{Aysha, Graham}]
J=[Person{Madeleine, Jenkins}, Person{Christina, Johnson}]
K=[Person{Jasmine, Knight}]
M=[Person{Freya, Montgomery}]
很难从这里开始,因为我需要进一步汇总以上内容以获得包含三个 entries/keys 的地图。期望的输出:
Map<String, List<Person>> result = ...
A-H = [Person{Helena, Graves}, Person{Aysha, Graham}, Person{Melissa, Carpenter}, Person{Marie, Daniel}, Person{Robin, French}, Person{Annie, Bradley}]
I-N = [Person{Jasmine, Knight}, Person{Madeleine, Jenkins}, Person{Christina, Johnson}, Person{Freya, Montgomery}]
O-Z = [Person{Phoebe, Reyes}, Person{Tamara, Wyatt}, Person{Lacey, Todd}, Person{Heather, Parker}, Person{Lauren, Wright}]
基本上,您只需要使用 Collectors.groupBy(Function)
进行分组,然后使用一个函数将每个 Person
分配到正确的组中:
/**
* This method is null-friendly
*/
String group(Person person) {
return Optional.ofNullable(person)
.map(Person::getFirstName)
.filter(name -> name.length() > 0)
.map(name -> name.charAt(0))
.map(ch -> {
if (ch >= 'A' && ch <= 'H') {
return "A-H";
} else if (ch > 'H' && ch <= 'N') {
return "I-N";
} else if (ch > 'N' && ch <= 'Z') {
return "O-Z";
}
return "*"; // In case the name starts with a character out of A-Z range
})
.orElse("none"); // In case there is empty/null firstName
}
Map<String, List<Person>> map = myList
.stream()
.collect(Collectors.groupingBy(this::group));
您应该稍微更改分类器函数以组合一系列字符。
另外,可能需要对entrySet()进行排序(或者采集到地图时使用SortedMap
/TreeMap
):
myList.stream()
.collect(Collectors.groupingBy(
p -> p.getLastName().charAt(0) < 'I' ? "A-H" :
p.getLastName().charAt(0) < 'O' ? "I-N" : "O-Z"
))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.forEach(System.out::println);
输出:
A-H=[Person {Helena Graves}, Person {Aysha Graham}, Person {Melissa Carpenter}, Person {Marie Daniel}, Person {Robin French}, Person {Annie Bradley}]
I-N=[Person {Jasmine Knight}, Person {Madeleine Jenkins}, Person {Christina Johnson}, Person {Freya Montgomery}]
O-Z=[Person {Phoebe Reyes}, Person {Tamara Wyatt}, Person {Lacey Todd}, Person {Heather Parker}, Person {Lauren Wright}]
我有一个简单的 class Person
:
class Person {
String firstName;
String lastName;
//getter, setter, constructor, toString
}
还有一个 Persons
的输入列表,例如:
List<Person> myList = List.of(
new Person("Helena", "Graves"),
new Person("Jasmine", "Knight"),
new Person("Phoebe", "Reyes"),
new Person("Aysha", "Graham"),
new Person("Madeleine", "Jenkins"),
new Person("Christina", "Johnson"),
new Person("Melissa", "Carpenter"),
new Person("Marie", "Daniel"),
new Person("Robin", "French"),
new Person("Tamara", "Wyatt"),
new Person("Freya", "Montgomery"),
new Person("Lacey", "Todd"),
new Person("Heather", "Parker"),
new Person("Lauren", "Wright"),
new Person("Annie", "Bradley")
);
现在我需要将上面的列表按此人姓氏的第一个字符进行分组,然后再次对这些组进行分组,以便所有在 A-H
之间开始的姓氏属于一个组,下一组属于那些从 I-N
开始,最后是 O-Z
.
我已经可以按姓氏的第一个字符对列表进行分组:
myList.stream()
.collect(Collectors.groupingBy(p -> String.valueOf(p.getLastName().charAt(0))))
.entrySet()
.forEach(System.out::println);
这给了我:
P=[Person{Heather, Parker}]
B=[Person{Annie, Bradley}]
R=[Person{Phoebe, Reyes}]
C=[Person{Melissa, Carpenter}]
T=[Person{Lacey, Todd}]
D=[Person{Marie, Daniel}]
F=[Person{Robin, French}]
W=[Person{Tamara, Wyatt}, Person{Lauren, Wright}]
G=[Person{Helena, Graves}, Person{Aysha, Graham}]
J=[Person{Madeleine, Jenkins}, Person{Christina, Johnson}]
K=[Person{Jasmine, Knight}]
M=[Person{Freya, Montgomery}]
很难从这里开始,因为我需要进一步汇总以上内容以获得包含三个 entries/keys 的地图。期望的输出:
Map<String, List<Person>> result = ...
A-H = [Person{Helena, Graves}, Person{Aysha, Graham}, Person{Melissa, Carpenter}, Person{Marie, Daniel}, Person{Robin, French}, Person{Annie, Bradley}]
I-N = [Person{Jasmine, Knight}, Person{Madeleine, Jenkins}, Person{Christina, Johnson}, Person{Freya, Montgomery}]
O-Z = [Person{Phoebe, Reyes}, Person{Tamara, Wyatt}, Person{Lacey, Todd}, Person{Heather, Parker}, Person{Lauren, Wright}]
基本上,您只需要使用 Collectors.groupBy(Function)
进行分组,然后使用一个函数将每个 Person
分配到正确的组中:
/**
* This method is null-friendly
*/
String group(Person person) {
return Optional.ofNullable(person)
.map(Person::getFirstName)
.filter(name -> name.length() > 0)
.map(name -> name.charAt(0))
.map(ch -> {
if (ch >= 'A' && ch <= 'H') {
return "A-H";
} else if (ch > 'H' && ch <= 'N') {
return "I-N";
} else if (ch > 'N' && ch <= 'Z') {
return "O-Z";
}
return "*"; // In case the name starts with a character out of A-Z range
})
.orElse("none"); // In case there is empty/null firstName
}
Map<String, List<Person>> map = myList
.stream()
.collect(Collectors.groupingBy(this::group));
您应该稍微更改分类器函数以组合一系列字符。
另外,可能需要对entrySet()进行排序(或者采集到地图时使用SortedMap
/TreeMap
):
myList.stream()
.collect(Collectors.groupingBy(
p -> p.getLastName().charAt(0) < 'I' ? "A-H" :
p.getLastName().charAt(0) < 'O' ? "I-N" : "O-Z"
))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.forEach(System.out::println);
输出:
A-H=[Person {Helena Graves}, Person {Aysha Graham}, Person {Melissa Carpenter}, Person {Marie Daniel}, Person {Robin French}, Person {Annie Bradley}]
I-N=[Person {Jasmine Knight}, Person {Madeleine Jenkins}, Person {Christina Johnson}, Person {Freya Montgomery}]
O-Z=[Person {Phoebe Reyes}, Person {Tamara Wyatt}, Person {Lacey Todd}, Person {Heather Parker}, Person {Lauren Wright}]