Элегантный способ создания плоской карты Наборы внутри группировки - PullRequest
0 голосов
/ 28 сентября 2018

Итак, у меня есть фрагмент кода, в котором я перебираю список данных.Каждый из них представляет собой ReportData, который содержит регистр с Long caseId и один Ruling.Каждый Ruling имеет один или несколько Payment.Я хочу иметь Map с caseId в качестве ключей и наборы платежей в качестве значений (т. Е. Map<Long, Set<Payments>>).

Случаи не являются уникальными по строкам, но случаи.

Другими словами, у меня может быть несколько строк с одним и тем же регистром, но они будут иметь уникальные решения.

Следующий код дает мне Map<Long, Set<Set<Payments>>>, что почти то, что я хочу, но я былизо всех сил пытаясь найти правильный способ flatMap окончательного набора в данном контексте.Я делал обходные пути, чтобы заставить логику работать правильно, используя эту карту как есть, но я бы очень хотел исправить алгоритм для правильного объединения набора платежей в один набор вместо создания набора.

Я искал и не мог найти проблему с такой же итерацией, хотя flatMapping с потоками Java кажется довольно популярной темой.

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

Ответы [ 3 ]

0 голосов
/ 28 сентября 2018

Другое решение 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())));
0 голосов
/ 28 сентября 2018

Самое чистое решение - определить собственный коллектор:

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.

0 голосов
/ 28 сентября 2018

Возможно, есть лучшие способы сделать это, но это лучшее, что я нашел:

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