Java 8 lambdas分组缩减和映射

时间:2015-06-10 13:45:11

标签: java java-8 java-stream

鉴于以下List类的Transaction,使用Java 8 lambdas,我希望获得List ResultantDTO,每个帐户类型一个。

public class Transaction {

    private final BigDecimal amount;
    private final String accountType;
    private final String accountNumber;

}

public class ResultantDTO {

    private final List<Transaction> transactionsForAccount;

    public ResultantDTO(List<Transaction> transactionsForAccount){
        this.transactionsForAccount = transactionsForAccount;
    }

}

到目前为止,我使用以下代码将List<Transaction>分组为accountType

Map<String, List<Transaction>> transactionsGroupedByAccountType = transactions
    .stream()
    .collect(groupingBy(Transaction::getAccountType));

如何返回List<ResultantDTO>,将List从每个地图键传递到构造函数中,每个ResultantDTO包含一个accountType

3 个答案:

答案 0 :(得分:5)

您可以在单流操作中执行此操作:

public List<ResultantDTO> convert(List<Transaction> transactions) {
    return transactions.stream().collect(
            collectingAndThen(
                groupingBy(
                        Transaction::getAccountType,
                        collectingAndThen(toList(), ResultantDTO::new)),
                map -> new ArrayList<>(map.values())));
}

此处collectingAndThen使用了两次:一次用于下游Collector将列表转换为ResultantDTO个对象,一次将结果映射转换为其值列表。

答案 1 :(得分:2)

假设您有:

static ResultantDTO from(List<Transaction> transactions) {...}

你可以写:

Map<String, List<Transaction>> transactionsGroupedByAccountType = transactions
    .stream()
    .collect(groupingBy(Transaction::getAccountType));

Map<String, ResultantDTO> result = transactionsGroupedByAccountType.entrySet().stream()
         .collect(toMap(Entry::getKey, e -> from(e.getValue)));

您可以在一个流中执行此操作,但这可能更简洁。

答案 2 :(得分:1)

您可以使用toMap收藏家:

Map<String, ResultantDTO> transactionsGroupedByAccountType = transactions
                .stream()
                .collect(toMap(Transaction::getAccountType,
                        t -> new ResultantDTO(t.getAmount(), 
                                              Stream.of(new SimpleEntry<>(t.getAccountNumber(), t.getAmount()))
                                                    .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue))),
                        (dt1, dt2) -> new ResultantDTO(dt1.getSumOfAmountForAccountType().add(dt2.getSumOfAmountForAccountType()), 
                                                       Stream.of(dt1.getAccountNumberToSumOfAmountForAccountNumberMap(), dt2.getAccountNumberToSumOfAmountForAccountNumberMap())
                                                             .flatMap(m -> m.entrySet().stream())
                                                             .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)))));

虽然它不太可读。也许您可以添加一个构造函数,该构造函数在Transaction类中使用单个ResultantDTO作为参数和合并:

 public ResultantDTO(Transaction t) {
     ///
 }

 static ResultantDTO merger(ResultantDTO r1, ResultantDTO r2) {
     ///
 }

并且更具可读性:

Map<String, ResultantDTO> transactionsGroupedByAccountType = transactions
                .stream()
                .collect(toMap(Transaction::getAccountType,
                               ResultantDTO::new,
                               ResultantDTO::merger));