分组时使用flatMap Sets Sets的优雅方式

时间:2018-09-28 08:13:13

标签: java java-8 java-stream

所以我有一段代码在其中遍历数据列表。每个案例都是一个ReportData,其中包含一个带有Long caseId和一个Ruling的案例。每个Ruling具有一个或多个Payment。我想使用一个Map作为键,并将付款集作为值(即caseId)作为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()
                )
            )));

3 个答案:

答案 0 :(得分:5)

另一个JDK8解决方案:

pycurl

或者从JDK9开始,您可以使用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;})); 收集器:

flatMapping

答案 1 :(得分:2)

最干净的解决方案是定义自己的收集器:

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操作将Set<Payment>项与相同的caseId合并。但是,如果需要大量合并,这可能会导致集合的很多副本。

另一种解决方案是使用下游收集器对嵌套的集合进行平面映射:

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中,然后将平面图列出为单个Set

答案 2 :(得分:1)

也许有更好的方法可以做到这一点,但这是我发现的最好方法:

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