Java 8 Лямбда - группировка и редукция объекта - PullRequest
3 голосов
/ 23 апреля 2020

У меня есть список транзакций, которые я хотел бы:

  • Первая группа по году
  • Затем группировка по типу для каждой транзакции в этом году
  • Затем преобразовать транзакции в объект Result, имеющий сумму всех транзакций в подгруппах.

Фрагменты моего кода выглядят следующим образом:

Map<Integer, Map<String, Result> res = transactions.stream().collect(Collectors
                            .groupingBy(Transaction::getYear,
                                groupingBy(Transaction::getType),
                                  reducing((a,b)-> new Result("YEAR_TYPE", a.getAmount() + b.getAmount()))
));

Класс транзакции:

class Transaction {

    private int year;
    private String type;
    private int value;
}

Класс результата:

class Result {

    private String group;
    private int amount;
}

кажется чтобы не работать, что я должен сделать, чтобы исправить это, убедившись, что он работает и на параллельных потоках?

Ответы [ 2 ]

2 голосов
/ 24 апреля 2020

В этом контексте Collectors.reducing поможет вам преобразовать два Transaction объекта в конечный объект того же типа. В вашем существующем коде вы могли сделать для отображения на Result тип, используя Collectors.mapping и затем пытаясь уменьшить его.

Но сокращение без идентификатора обеспечивает и Optional упакованное значение для возможного отсутствия. Следовательно, ваш код выглядел бы так:

Map<Integer, Map<String, Optional<Result>>> res = transactions.stream()
        .collect(Collectors.groupingBy(Transaction::getYear,
                Collectors.groupingBy(Transaction::getType,
                        Collectors.mapping(t -> new Result("YEAR_TYPE", t.getValue()),
                                Collectors.reducing((a, b) ->
                                        new Result(a.getGroup(), a.getAmount() + b.getAmount()))))));

, благодаря Хольгеру , можно еще больше упростить это

… и вместо Collectors.mapping(func, Collectors.reducing(op)) вы можно использовать Collectors.reducing(id, func, op)


Вместо использования этого и комбинации Collectors.grouping и Collectors.reducing, преобразуйте logi c, чтобы использовать Collectors.toMap как:

Map<Integer, Map<String, Result>> result = transactions.stream()
        .collect(Collectors.groupingBy(Transaction::getYear,
                Collectors.toMap(Transaction::getType,
                        t -> new Result("YEAR_TYPE", t.getValue()),
                        (a, b) -> new Result(a.getGroup(), a.getAmount() + b.getAmount()))));

Ответ будет завершен с последующим прочтением Java Потоки: замена groupingBy и сокращение до toMap .

1 голос
/ 23 апреля 2020

Я бы использовал собственный коллектор:

Collector<Transaction, Result, Result> resultCollector =
 Collector.of(Result::new, // what is the result of this collector

  (a, b) -> { a.setAmount( a.getAmount() + b.getValue());
              a.setGroup("YEAR_TYPE"); }, // how to accumulate a result from a transaction

  (l, r) -> { l.setAmount(l.getAmount() + r.getAmount()); return l; }); // how to combine two 
                                                                        // result instances 
                                                                        // (used in parallel streams)

, тогда вы можете использовать коллектор, чтобы получить карту:

Map<Integer, Map<String, Result>> collect = transactions.parallelStream().collect(
       groupingBy(Transaction::getYear,
               groupingBy(Transaction::getType, resultCollector) ) );
...