使用Java 8流的复杂聚合(Complex aggregation using Java 8 streams)

编程入门 行业动态 更新时间:2024-10-26 23:27:58
使用Java 8流的复杂聚合(Complex aggregation using Java 8 streams)

给定一个类的Item:

public class Item { private String field1; private String field2; private String field3; private Integer field4; // getters, constructor... }

另一个类组(field1和field2存储Item中的等效字段):

public class Group { private String field1; private String field2; }

我有一个List<Item> ,我需要将它聚合到以下结构的映射中:

Map<Group, Map<Field3, List<Field4>>>

示例数据:

Field1 | Field2 | Field3 | Field4 ------ | ------ | ------ | ------ "f1" | "f2" | "a" | 1 "f1" | "f2" | "a" | 2 "f1" | "f2" | "a" | 3 "f1" | "f2" | "b" | 4 "f1" | "f2" | "b" | 5 "f1" | "f2" | "c" | 6 "f1a" | "f2a" | "a" | 7 "f1a" | "f2a" | "a" | 8

预期结果如下所示:

Group(field1=f1a, field2=f2a)={b=[7, 8]}, Group(field1=f1, field2=f2)={a=[1, 2, 3], b=[4, 5], c=[6]}

到目前为止,我已经能够通过Field1,Field2和Field3进行聚合,因此我具有以下结构(其中GroupEx表示包含Field1,Field2和Field3的POJO):

Map<GroupEx, List<Field4>>

以这种方式聚合的代码是:

Map<GroupEx, List<Integer>> aggregated = items.stream() .collect(Collectors.groupingBy(item -> new GroupEx(x.getField1(), x.getField2(), x.getField3()) , Collectors.mapping(Item::getField4, Collectors.toList())));

我努力让语法正确,让我按照Field1和Field2进行分组,然后按照我需要的方式将Field3和Field4分组到地图中。

“长手”的语法是:

Map<Group<String, String>, Map<String, List<Integer>>> aggregated = new HashMap<>(); for (Item item : items) { Group key = new Group(item.getField1(), item.getField2()); Map<String, List<Integer>> field3Map = aggregated.get(key); if (field3Map == null) { field3Map = new HashMap<>(); aggregated.put(key, field3Map); } List<Integer> field4s = field3Map.get(item.getField3()); if (field4s == null) { field4s = new ArrayList<>(); field3Map.put(item.getField3(), field4s); } field4s.add(item.getField4()); }

有人能够告诉我如何实现我的目标分组?

Given a class Item:

public class Item { private String field1; private String field2; private String field3; private Integer field4; // getters, constructor... }

And another class Group (field1 and field2 store the equivalent fields from Item):

public class Group { private String field1; private String field2; }

I have a List<Item> which I need to aggregate into a map of the following structure:

Map<Group, Map<Field3, List<Field4>>>

Example data:

Field1 | Field2 | Field3 | Field4 ------ | ------ | ------ | ------ "f1" | "f2" | "a" | 1 "f1" | "f2" | "a" | 2 "f1" | "f2" | "a" | 3 "f1" | "f2" | "b" | 4 "f1" | "f2" | "b" | 5 "f1" | "f2" | "c" | 6 "f1a" | "f2a" | "a" | 7 "f1a" | "f2a" | "a" | 8

The expected result would look like:

Group(field1=f1a, field2=f2a)={b=[7, 8]}, Group(field1=f1, field2=f2)={a=[1, 2, 3], b=[4, 5], c=[6]}

So far I have been able to aggregate by Field1, Field2 and Field3, such that I have the following structure (where GroupEx represents a POJO that holds Field1, Field2 and Field3):

Map<GroupEx, List<Field4>>

The code to aggregate in this way is:

Map<GroupEx, List<Integer>> aggregated = items.stream() .collect(Collectors.groupingBy(item -> new GroupEx(x.getField1(), x.getField2(), x.getField3()) , Collectors.mapping(Item::getField4, Collectors.toList())));

I'm struggling to get the syntax correct to allow me to group by Field1 and Field2, and to then group by Field3 and Field4 into a map in the way I need.

The "long hand" syntax is:

Map<Group<String, String>, Map<String, List<Integer>>> aggregated = new HashMap<>(); for (Item item : items) { Group key = new Group(item.getField1(), item.getField2()); Map<String, List<Integer>> field3Map = aggregated.get(key); if (field3Map == null) { field3Map = new HashMap<>(); aggregated.put(key, field3Map); } List<Integer> field4s = field3Map.get(item.getField3()); if (field4s == null) { field4s = new ArrayList<>(); field3Map.put(item.getField3(), field4s); } field4s.add(item.getField4()); }

Is someone able to show me how my target grouping can be achieved?

最满意答案

这是下游收集器功能的好处。

import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.mapping; import static java.util.stream.Collectors.toList; ... List<Item> list = .... Map<Group, Map<String, List<Integer>>> map = list.stream().collect(groupingBy(i -> new Group(i.getField1(), i.getField2()), groupingBy(Item::getField3, mapping(Item::getField4, toList()))));

首先,按照他们的Group字段对这些项目进行Group (此时您是一个Map<Group, List<Item>> ),然后将每个值( List<Item> )映射到一个地图, Map<Group, Map<Field3, List<Item>> )。

然后通过field4映射第二个映射中的值,并将它们收集到列表中,最终得到一个Map<Group, Map<Field3, List<Field4>> 。

给你的输入,它输出:

{Group{field1='f1a', field2='f2a'}={a=[7, 8]}, Group{field1='f1', field2='f2'}={a=[1, 2, 3], b=[4, 5], c=[6]}}

This is where the downstream collectors feature comes handy.

import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.mapping; import static java.util.stream.Collectors.toList; ... List<Item> list = .... Map<Group, Map<String, List<Integer>>> map = list.stream().collect(groupingBy(i -> new Group(i.getField1(), i.getField2()), groupingBy(Item::getField3, mapping(Item::getField4, toList()))));

First, you group the items by their Group field (you a Map<Group, List<Item>> at this point), then you map each value (List<Item>) to a map again where you group by the field3 (Map<Group, Map<Field3, List<Item>>).

Then you map the values in the second map by the field4 and you collect them into a list, to finally get a Map<Group, Map<Field3, List<Field4>>.

Given your input, it outputs:

{Group{field1='f1a', field2='f2a'}={a=[7, 8]}, Group{field1='f1', field2='f2'}={a=[1, 2, 3], b=[4, 5], c=[6]}}

更多推荐

本文发布于:2023-08-07 21:40:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1466455.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:Complex   Java   streams   aggregation

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!