从列表

时间:2017-02-17 17:10:17

标签: java java-8 java-stream

我对Java 8相对较新,并试图绕过流。 我从数据库中查询返回以下内容:

String companyName | 
String clientName | 
BigDecimal amount | 
String transactionType (either credit or debit) | 
long numberOfTransactions

我将每个行存储在此对象中,使用transactionType字段的值来确定哪个creditAmount或debitAmount量被填充

public RowForCsv{
    private String companyName;
    private String customerName;
    private BigDecimal creditAmount;
    private BigDecimal debitAmount;
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((companyName == null) ? 0 : companyName.hashCode());
        result = prime * result + ((customerName == null) ? 0 : customerName.hashCode());
        return result;
    }
}

我需要使用这些数据为与每个公司名称相关的交易制作单独的CSV文件,这意味着最终我想要

`Map<String companyName, List<RowForCsv> associatedTransactions>`

由于客户可以同时发放信用和借方,我还希望将具有相同客户和公司名称的单独RowForCsv对象合并到一个RowForCsv对象中。

以下是我尝试的内容:

//Get Map<name, list of transactions>
Map<String, List<ReconciliationRecordForCsv>> mapByCompanyName = records.stream().collect(Collectors.groupingBy(ReconciliationRecordForCsv::getCompanyName));
// Merge duplicates
mapByCompanyName.replaceAll((k, v) -> {
    v.stream().collect(Collectors.collectingAndThen(
        Collectors.groupingBy(RowForCsv::hashCode), 
            Collectors.collectingAndThen(Collectors.reducing((a, b) -> mergeCreditAndDebitRecords(a, b)), Optional::get)), 
                m -> new ArrayList<>(m.values()));
    });

这是我的合并功能,我认为我误解了......的概念。

private RowForCsv mergeCreditAndDebitRecords(RowForCsv first, RowForCsv second) {
    RowForCsv.Builder merged = new RowForCsv.Builder();
    return merged.companyName(first.getCompanyName())
            .customerName(first.getCustomerName())
            .creditAmount(getLarger(first.getCreditAmount(), second.getCreditAmount()))
            .debitAmount(getLarger(first.getDebitAmount(), second.getDebitAmount()))
            .build();
}

此时我收到与没有类型的替换所有(k,v)有关的错误,collectAndThens都抛出与其链接相关的错误,并且合并函数对于类型无效(对象,对象)。

我有一种感觉,我以错误的方式接近这一点,但我不知道自己应该采取哪些不同的做法,任何指导都会受到高度赞赏。

2 个答案:

答案 0 :(得分:1)

我认为这可能是一个解决方案:

final Map<String, List<Optional<RowForCsv>>> collect1 =
            rows.stream().collect(Collectors.groupingBy(RowForCsv::getCompanyName, Collectors.collectingAndThen(Collectors.toList(), byBank -> {
                return byBank.stream() //
                    .collect(Collectors.groupingBy(RowForCsv::getCustomerName)).values().stream()
                    .map(byUser -> byUser.stream().reduce((r1, r2) -> r2)).collect(Collectors.toList()); //
            })));

另一种方法可能是&#34;减少&#34;初始行,以便每个用户拥有唯一的行,然后是groupBy公司。

如果我有更多时间,我会回到这里。有趣的问题。也许你可以尝试从db改进你的查询。从数据库侧进行分组总是更快。

答案 1 :(得分:0)

如果我正确理解您的问题:您希望按公司名称按记录分组,并使用相同的客户名称合并最大creditAmount / debitAmount的交易。这是AbacusUtil

的解决方案
// merge two records with same customer name by using the maximum creditAmount/debitAmount
BinaryOperator<RowForCsv> mergeFunction = (a, b) -> {
    RowForCsv c = new RowForCsv();
    c.companyName = a.companyName;
    c.customerName = a.customerName;
    c.creditAmount = N.max(a.creditAmount, b.creditAmount);
    c.debitAmount = N.max(a.creditAmount, b.creditAmount);
    return c;
};

Map<String, Collection<RowForCsv>> mergedAmountByCompanyName = 
  Stream.of(records).groupBy(e -> e.getCompanyName())
  .toMap(e -> e.getKey(),
         e -> Stream.of(e.getValue())
             .toMap(e2 -> e2.getCustomerName(), e2 -> e2, mergeFunction).values());