简体   繁体   English

优雅的方式到flatMapBy中的flatMap集

[英]Elegant way to flatMap Set of Sets inside groupingBy

So I have a piece of code where I'm iterating over a list of data. 所以我有一段代码,我在迭代一个数据列表。 Each one is a ReportData that contains a case with a Long caseId and one Ruling . 每一个都是一个ReportData ,它包含一个具有Long caseId和一个Ruling的案例。 Each Ruling has one or more Payment . 每项Ruling都有一项或多项Payment I want to have a Map with the caseId as keys and sets of payments as values (ie a Map<Long, Set<Payments>> ). 我希望将带有caseIdMap作为键和付款组作为值(即Map<Long, Set<Payments>> )。

Cases are not unique across rows, but cases are. 案例在行之间并不是唯一的,但案例是。

In other words, I can have several rows with the same case, but they will have unique rulings. 换句话说,我可以有几个具有相同大小写的行,但它们将具有唯一的规则。

The following code gets me a Map<Long, Set<Set<Payments>>> which is almost what I want, but I've been struggling to find the correct way to flatMap the final set in the given context. 下面的代码给我一个Map<Long, Set<Set<Payments>>>这几乎就是我想要的,但我一直在努力找到在给定上下文中flatMap最终集合的正确方法。 I've been doing workarounds to make the logic work correctly using this map as is, but I'd very much like to fix the algorithm to correctly combine the set of payments into one single set instead of creating a set of sets. 我一直在做着使用这个映射使逻辑正常工作的解决方法,但是我非常想修复算法以将这组付款正确地组合成一个集合,而不是创建一组集合。

I've searched around and couldn't find a problem with the same kind of iteration, although flatMapping with Java streams seems like a somewhat popular topic. 虽然使用Java流进行flatMapping似乎是一个有点热门的话题,但我一直在搜索并找不到相同类型的迭代问题。

rowData.stream()
        .collect(Collectors.groupingBy(
            r -> r.case.getCaseId(),
            Collectors.mapping(
                r -> r.getRuling(),
                Collectors.mapping(ruling->
                    ruling.getPayments(),
                    Collectors.toSet()
                )
            )));

Another JDK8 solution: 另一个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;}));

or as of JDK9 you can use the flatMapping collector: 或者从JDK9开始,您可以使用flatMapping收集器:

rowData.stream()
       .collect(Collectors.groupingBy(r -> r.Case.getCaseId(), 
              Collectors.flatMapping(e -> e.getRuling().getPayments().stream(), 
                        Collectors.toSet())));

The cleanest solution is to define your own collector: 最干净的解决方案是定义您自己的收集器:

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; })
        ));

Two other solutions to which I thought first but are actually less efficient and readable, but still avoid constructing the intermediate Map : 我首先想到的另外两个解决方案但实际上效率和可读性较低,但仍然避免构建中间Map

Merging the inner sets using Collectors.reducing() : 使用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;
                        })
        ));

where the reducing operation will merge the Set<Payment> of entries with the same caseId . reducing操作将合并具有相同caseId的条目的Set<Payment> This can however cause a lot of copies of the sets if you have a lot of merges needed. 但是,如果您需要进行大量合并,则可能会导致很多副本的副本。

Another solution is with a downstream collector that flatmaps the nested collections: 另一种解决方案是使用下游收集器来平铺嵌套集合:

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())))
        );

Basically it puts all sets of matching caseId together in a List , then flatmaps that list into a single Set . 基本上它将所有匹配的caseId集合放在一个List ,然后将列表的flatmaps放入一个Set

There are probably better ways to do this, but this is the best I found: 可能有更好的方法来做到这一点,但这是我发现的最好的方法:

 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM