将 streams.groupingBy() 的嵌套映射转换为 POJO 列表
Convert nested map of streams.groupingBy() into list of POJOs
我想将由 java streams + groupingBy 创建的嵌套映射结构转换为 POJO 列表,其中每个 POJO 代表其中一个组,并且还包含该组的所有匹配对象。
我有以下代码:为了方便,我在这里使用project lombok(@Builder、@Data)。如果这让我感到困惑,请告诉我。
我的目标是防止两点发生:
- 具有深度嵌套的地图和
- 因此:如果条目
相反,我想要一个简洁明了的 POJO 列表,它代表分组并方便地保存每个组的匹配条目。
如果需要,可以在 GitHub 到 运行 上找到代码。
编辑 1:
我再次更新了代码以删除姓氏并添加另一个“Gerrit”对象以使两个对象具有相同的分组。我希望这能让意图更清晰。
编辑 2:
我再次更新了代码以在不属于分组的 Person 上添加 属性。
我正在寻找这样的输出:
[
Grouping(firstname=Jane, age=24, homeCountry=USA, persons=[Person(firstname=Jane, age=24, homeCountry=USA, favoriteColor=yellow)]),
Grouping(firstname=gerrit, age=24, homeCountry=germany, persons=[
Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=blue), Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=green)
])
]
public class ConvertMapStreamToPojo {
@Data
@Builder
static class Person {
private String firstname;
private int age;
private String homeCountry;
private String favoriteColor;
}
@Data
static class Grouping {
private String firstname;
private int age;
private String homeCountry;
List<Person> persons;
}
public static void main(String[] args) {
Person gerrit = Person.builder()
.firstname("gerrit")
.age(24)
.homeCountry("germany")
.favoriteColor("blue")
.build();
Person anotherGerrit = Person.builder()
.firstname("gerrit")
.age(24)
.homeCountry("germany")
.favoriteColor("green")
.build();
Person janeDoe = Person.builder()
.firstname("Jane")
.age(25)
.homeCountry("USA")
.favoriteColor("yellow")
.build();
List<Person> persons = Arrays.asList(gerrit, anotherGerrit, janeDoe);
Map<String, Map<Integer, Map<String, List<Person>>>> nestedGroupings = persons.stream()
.collect(
Collectors.groupingBy(Person::getFirstname,
Collectors.groupingBy(Person::getAge,
Collectors.groupingBy(Person::getHomeCountry)
)
)
);
/**
* Convert the nested maps into a List<Groupings> where each group
* holds a list of all matching persons
*/
List<Grouping> groupings = new ArrayList<>();
for (Grouping grouping: groupings) {
String message = String.format("Grouping for firstname %s age %s and country %s", grouping.getFirstname(), grouping.getAge(), grouping.getHomeCountry());
System.out.println(message);
System.out.println("Number of persons inside this grouping: " + grouping.getPersons().size());
}
// example groupings
/**
*
* [
* Grouping(firstname=Jane, age=24, homeCountry=USA, persons=[Person(firstname=Jane, age=24, homeCountry=USA, favoriteColor=yellow)]),
* Grouping(firstname=gerrit, age=24, homeCountry=germany, persons=[
* Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=blue), Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=green)
* ])
* ]
*
*/
}
}
如果您在解决方案中添加重复的人,那么 grouping
变量将仅包含非重复值。我相信这不是你想要的正确意图。
为了实现您也持有重复项,您可以在 Person
class 中创建一个“分组键”以适当地对值进行分组(甚至 use records,具体取决于您的 Java版本)。
@Data
@Builder
static class Person {
private String firstname;
private String lastname;
private int age;
private String homeCountry;
String groupingKey() {
return firstname + "/" + lastname + "/" + age + "/" + homeCountry;
}
}
@Data
@Builder
static class Grouping {
private String firstname;
private String lastname;
private int age;
private String homeCountry;
List<Person> persons;
}
public static void main(String[] args) {
Person johnDoe = Person.builder()
.firstname("John")
.lastname("Doe")
.age(25)
.homeCountry("USA")
.build();
Person janeDoe = Person.builder()
.firstname("Jane")
.lastname("Doe")
.age(25)
.homeCountry("USA")
.build();
Person duplicateJaneDoe = Person.builder()
.firstname("Jane")
.lastname("Doe")
.age(25)
.homeCountry("USA")
.build();
List<Person> persons = Arrays.asList(johnDoe, janeDoe, duplicateJaneDoe);
Map<String, List<Person>> groupedPersons = persons.stream()
.collect(Collectors.groupingBy(Person::groupingKey));
// Since now you have grouped persons, you can go through map entries
// and fill your Grouping object list by splitting the key or just taking
// the first object from the person list.
List<Grouping> groupings = new ArrayList<>();
groupedPersons.forEach((key, value) -> {
String[] personBits = key.split("/");
Grouping grouping = new Grouping.GroupingBuilder()
.firstname(personBits[0])
.lastname(personBits[1])
.age(Integer.parseInt(personBits[2]))
.homeCountry(personBits[3])
.persons(value)
.build();
groupings.add(grouping);
});
}
我不太确定 Grouping
对象的用途,因为将地图转换为 List<Grouping>
人员列表实际上将包含重复人员。
这可以通过简单的 groupingBy
人并将 Map.Entry
转换为 Grouping
来实现。
更新
如果 Grouping
的“键”部分的字段少于 Person
(favoriteColor
最近已添加到 Person
),则值得实现另一个代表 [的键的 POJO =14=]:
@Data
@AllArgsConstructor
static class GroupingKey {
private String firstname;
private int age;
private String homeCountry;
public GroupingKey(Person person) {
this(person.firstname, person.age, person.homeCountry);
}
}
然后 GroupingKey
的实例可以在 Grouping
中使用以避免重复。
假设在 Grouping
中实现了全参数构造函数和映射构造函数
@Data
@AllArgsConstructor
static class Grouping {
// Not needed in toString, related fields are available in Person instances
@ToString.Exclude
private GroupingKey key;
List<Person> persons;
public Grouping(Map.Entry<GroupingKey, List<Person>> e) {
this(e.getKey(), e.getValue());
}
}
那么实现可以如下:
List<Grouping> groupings = persons.stream()
.collect(Collectors.groupingBy(GroupingKey::new))
.entrySet().stream()
.map(Grouping::new)
.collect(Collectors.toList());
groupings.forEach(System.out::println);
输出(测试数据略有改动,去掉关键部分):
Grouping(persons=[Person(firstname=Jane, age=24, homeCountry=USA, favoriteColor=Azure)])
Grouping(persons=[Person(firstname=gerrit, age=24, homeCountry=USA, favoriteColor=Red)])
Grouping(persons=[Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=Black), Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=Green)])
我想将由 java streams + groupingBy 创建的嵌套映射结构转换为 POJO 列表,其中每个 POJO 代表其中一个组,并且还包含该组的所有匹配对象。
我有以下代码:为了方便,我在这里使用project lombok(@Builder、@Data)。如果这让我感到困惑,请告诉我。
我的目标是防止两点发生:
- 具有深度嵌套的地图和
- 因此:如果条目
相反,我想要一个简洁明了的 POJO 列表,它代表分组并方便地保存每个组的匹配条目。
如果需要,可以在 GitHub 到 运行 上找到代码。
编辑 1: 我再次更新了代码以删除姓氏并添加另一个“Gerrit”对象以使两个对象具有相同的分组。我希望这能让意图更清晰。
编辑 2: 我再次更新了代码以在不属于分组的 Person 上添加 属性。
我正在寻找这样的输出:
[
Grouping(firstname=Jane, age=24, homeCountry=USA, persons=[Person(firstname=Jane, age=24, homeCountry=USA, favoriteColor=yellow)]),
Grouping(firstname=gerrit, age=24, homeCountry=germany, persons=[
Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=blue), Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=green)
])
]
public class ConvertMapStreamToPojo {
@Data
@Builder
static class Person {
private String firstname;
private int age;
private String homeCountry;
private String favoriteColor;
}
@Data
static class Grouping {
private String firstname;
private int age;
private String homeCountry;
List<Person> persons;
}
public static void main(String[] args) {
Person gerrit = Person.builder()
.firstname("gerrit")
.age(24)
.homeCountry("germany")
.favoriteColor("blue")
.build();
Person anotherGerrit = Person.builder()
.firstname("gerrit")
.age(24)
.homeCountry("germany")
.favoriteColor("green")
.build();
Person janeDoe = Person.builder()
.firstname("Jane")
.age(25)
.homeCountry("USA")
.favoriteColor("yellow")
.build();
List<Person> persons = Arrays.asList(gerrit, anotherGerrit, janeDoe);
Map<String, Map<Integer, Map<String, List<Person>>>> nestedGroupings = persons.stream()
.collect(
Collectors.groupingBy(Person::getFirstname,
Collectors.groupingBy(Person::getAge,
Collectors.groupingBy(Person::getHomeCountry)
)
)
);
/**
* Convert the nested maps into a List<Groupings> where each group
* holds a list of all matching persons
*/
List<Grouping> groupings = new ArrayList<>();
for (Grouping grouping: groupings) {
String message = String.format("Grouping for firstname %s age %s and country %s", grouping.getFirstname(), grouping.getAge(), grouping.getHomeCountry());
System.out.println(message);
System.out.println("Number of persons inside this grouping: " + grouping.getPersons().size());
}
// example groupings
/**
*
* [
* Grouping(firstname=Jane, age=24, homeCountry=USA, persons=[Person(firstname=Jane, age=24, homeCountry=USA, favoriteColor=yellow)]),
* Grouping(firstname=gerrit, age=24, homeCountry=germany, persons=[
* Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=blue), Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=green)
* ])
* ]
*
*/
}
}
如果您在解决方案中添加重复的人,那么 grouping
变量将仅包含非重复值。我相信这不是你想要的正确意图。
为了实现您也持有重复项,您可以在 Person
class 中创建一个“分组键”以适当地对值进行分组(甚至 use records,具体取决于您的 Java版本)。
@Data
@Builder
static class Person {
private String firstname;
private String lastname;
private int age;
private String homeCountry;
String groupingKey() {
return firstname + "/" + lastname + "/" + age + "/" + homeCountry;
}
}
@Data
@Builder
static class Grouping {
private String firstname;
private String lastname;
private int age;
private String homeCountry;
List<Person> persons;
}
public static void main(String[] args) {
Person johnDoe = Person.builder()
.firstname("John")
.lastname("Doe")
.age(25)
.homeCountry("USA")
.build();
Person janeDoe = Person.builder()
.firstname("Jane")
.lastname("Doe")
.age(25)
.homeCountry("USA")
.build();
Person duplicateJaneDoe = Person.builder()
.firstname("Jane")
.lastname("Doe")
.age(25)
.homeCountry("USA")
.build();
List<Person> persons = Arrays.asList(johnDoe, janeDoe, duplicateJaneDoe);
Map<String, List<Person>> groupedPersons = persons.stream()
.collect(Collectors.groupingBy(Person::groupingKey));
// Since now you have grouped persons, you can go through map entries
// and fill your Grouping object list by splitting the key or just taking
// the first object from the person list.
List<Grouping> groupings = new ArrayList<>();
groupedPersons.forEach((key, value) -> {
String[] personBits = key.split("/");
Grouping grouping = new Grouping.GroupingBuilder()
.firstname(personBits[0])
.lastname(personBits[1])
.age(Integer.parseInt(personBits[2]))
.homeCountry(personBits[3])
.persons(value)
.build();
groupings.add(grouping);
});
}
我不太确定 Grouping
对象的用途,因为将地图转换为 List<Grouping>
人员列表实际上将包含重复人员。
这可以通过简单的 groupingBy
人并将 Map.Entry
转换为 Grouping
来实现。
更新
如果 Grouping
的“键”部分的字段少于 Person
(favoriteColor
最近已添加到 Person
),则值得实现另一个代表 [的键的 POJO =14=]:
@Data
@AllArgsConstructor
static class GroupingKey {
private String firstname;
private int age;
private String homeCountry;
public GroupingKey(Person person) {
this(person.firstname, person.age, person.homeCountry);
}
}
然后 GroupingKey
的实例可以在 Grouping
中使用以避免重复。
假设在 Grouping
@Data
@AllArgsConstructor
static class Grouping {
// Not needed in toString, related fields are available in Person instances
@ToString.Exclude
private GroupingKey key;
List<Person> persons;
public Grouping(Map.Entry<GroupingKey, List<Person>> e) {
this(e.getKey(), e.getValue());
}
}
那么实现可以如下:
List<Grouping> groupings = persons.stream()
.collect(Collectors.groupingBy(GroupingKey::new))
.entrySet().stream()
.map(Grouping::new)
.collect(Collectors.toList());
groupings.forEach(System.out::println);
输出(测试数据略有改动,去掉关键部分):
Grouping(persons=[Person(firstname=Jane, age=24, homeCountry=USA, favoriteColor=Azure)])
Grouping(persons=[Person(firstname=gerrit, age=24, homeCountry=USA, favoriteColor=Red)])
Grouping(persons=[Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=Black), Person(firstname=gerrit, age=24, homeCountry=germany, favoriteColor=Green)])