[英]Elegant way to flatMap Set of Sets inside groupingBy
所以我有一段代码,我在迭代一个数据列表。 每一个都是一个ReportData
,它包含一个具有Long caseId
和一个Ruling
的案例。 每项Ruling
都有一项或多项Payment
。 我希望将带有caseId
的Map
作为键和付款组作为值(即Map<Long, Set<Payments>>
)。
案例在行之间并不是唯一的,但案例是。
换句话说,我可以有几个具有相同大小写的行,但它们将具有唯一的规则。
下面的代码给我一个Map<Long, Set<Set<Payments>>>
这几乎就是我想要的,但我一直在努力找到在给定上下文中flatMap最终集合的正确方法。 我一直在做着使用这个映射使逻辑正常工作的解决方法,但是我非常想修复算法以将这组付款正确地组合成一个集合,而不是创建一组集合。
虽然使用Java流进行flatMapping似乎是一个有点热门的话题,但我一直在搜索并找不到相同类型的迭代问题。
rowData.stream()
.collect(Collectors.groupingBy(
r -> r.case.getCaseId(),
Collectors.mapping(
r -> r.getRuling(),
Collectors.mapping(ruling->
ruling.getPayments(),
Collectors.toSet()
)
)));
另一个JDK8解决方案:
Map<Long, Set<Payment>> resultSet =
rowData.stream()
.collect(Collectors.toMap(p -> p.Case.getCaseId(),
p -> new HashSet<>(p.getRuling().getPayments()),
(l, r) -> { l.addAll(r);return l;}));
或者从JDK9开始,您可以使用flatMapping
收集器:
rowData.stream()
.collect(Collectors.groupingBy(r -> r.Case.getCaseId(),
Collectors.flatMapping(e -> e.getRuling().getPayments().stream(),
Collectors.toSet())));
最干净的解决方案是定义您自己的收集器:
Map<Long, Set<Payment>> result = rowData.stream()
.collect(Collectors.groupingBy(
ReportData::getCaseId,
Collector.of(HashSet::new,
(s, r) -> s.addAll(r.getRuling().getPayments()),
(s1, s2) -> { s1.addAll(s2); return s1; })
));
我首先想到的另外两个解决方案但实际上效率和可读性较低,但仍然避免构建中间Map
:
使用Collectors.reducing()
合并内部集合:
Map<Long, Set<Payment>> result = rowData.stream()
.collect(Collectors.groupingBy(
ReportData::getCaseId,
Collectors.reducing(Collections.emptySet(),
r -> r.getRuling().getPayments(),
(s1, s2) -> {
Set<Payment> r = new HashSet<>(s1);
r.addAll(s2);
return r;
})
));
reducing
操作将合并具有相同caseId
的条目的Set<Payment>
。 但是,如果您需要进行大量合并,则可能会导致很多副本的副本。
另一种解决方案是使用下游收集器来平铺嵌套集合:
Map<Long, Set<Payment>> result = rowData.stream()
.collect(Collectors.groupingBy(
ReportData::getCaseId,
Collectors.collectingAndThen(
Collectors.mapping(r -> r.getRuling().getPayments(), Collectors.toList()),
s -> s.stream().flatMap(Set::stream).collect(Collectors.toSet())))
);
基本上它将所有匹配的caseId
集合放在一个List
,然后将列表的flatmaps放入一个Set
。
可能有更好的方法来做到这一点,但这是我发现的最好的方法:
Map<Long, Set<Payment>> result =
rowData.stream()
// First group by caseIds.
.collect(Collectors.groupingBy(r -> r.case.getCaseId()))
.entrySet().stream()
// By streaming over the entrySet, I map the values to the set of payments.
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().stream()
.flatMap(r -> r.getRuling().getPayments().stream())
.collect(Collectors.toSet())));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.