繁体   English   中英

在 java 中使用减少

[英]Using reducing in java

这是原始问题:

使用自定义 Object 作为 Java 中的缩减类型创建地图的 Map

它需要获取一个 Map<String,Map<String, MySumObject>> 来将金额相加,同时先按 LoanType 然后按 LoanCurrency 分组。

class MyObject {
    String loanType;
    String loanCurrency;
    BigDecimal amountPaid;
    BigDecimal amountRemaining;
}

class MySumObject {
    BigDecimal paidSum;
    BigDecimal remainingSum;
}

基于 Alexander Ivanchenko 的解决方案,使用

Collector.of(
                    MySumObject::new,
                    MySumObject::addLoan,
                    MySumObject::merge
                )

我得到了下面的代码

list.stream().collect(groupingBy(MyObject::getLoanType,
                      groupingBy(MyObject::getLoanCurrency,
                            Collector.of(
                                    MySumObject::new,
                                    (mySumObject, myObject) -> {
                                        mySumObject.setPaidSum(mySumObject.getPaidSum().add(myObject.getAmountPaid()));
                                        mySumObject.setRemainingSum(mySumObject.getRemainingSum().add(myObject.getAmountRemaining()));
                                        },
                                    (mySumObject1, mySumObject2) -> {
                                        mySumObject1.setPaidSum(mySumObject1.getPaidSum().add(mySumObject2.getPaidSum()));
                                        mySumObject1.setRemainingSum(mySumObject1.getRemainingSum().add(mySumObject2.getRemainingSum()));
                                        return mySumObject1;
                                    }
                            )
                            )
                      ));

然后我想用减少来让它工作。但不管groupingBy如何,它都会把所有东西都加起来。 不知道哪一部分是错的。

    List<MyObject> list = List.of(
            new MyObject("Type1", "Currency1", BigDecimal.valueOf(10), BigDecimal.valueOf(100)),
            new MyObject("Type1", "Currency1", BigDecimal.valueOf(10), BigDecimal.valueOf(100)),
            new MyObject("Type2", "Currency2", BigDecimal.valueOf(20), BigDecimal.valueOf(200)),
            new MyObject("Type3", "Currency3", BigDecimal.valueOf(30), BigDecimal.valueOf(300)),
            new MyObject("Type4", "Currency4", BigDecimal.valueOf(40), BigDecimal.valueOf(400)));

list.stream().collect(groupingBy(MyObject::getLoanType,
                                groupingBy(MyObject::getLoanCurrency,
                                        reducing(new MySumObject(
                                                        BigDecimal.ZERO,
                                                        BigDecimal.ZERO),
                                                (myObject) -> new MySumObject(
                                                        myObject.getAmountPaid(),
                                                        myObject.getAmountRemaining()),
                                                (mySumObject1, mySumObject2) -> {
                                                    mySumObject1.setPaidSum(mySumObject1.getPaidSum().add(mySumObject2.getPaidSum()));
                                                    mySumObject1.setRemainingSum(mySumObject1.getRemainingSum().add(mySumObject2.getRemainingSum()));
                                                    return mySumObject1;
                                                }
                                                )
                                        )
                                ));

这是不正确的 output。

Type2={Currency2=MySumObject(paidSum=110, remainingSum=1100)}
Type3={Currency3=MySumObject(paidSum=110, remainingSum=1100)}
Type4={Currency4=MySumObject(paidSum=110, remainingSum=1100)}
Type1={Currency1=MySumObject(paidSum=110, remainingSum=1100)}

操作reduce()意味着通过对不可变对象执行归约来折叠 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ(收集器归约reducing() )也是如此)。 另一方面, collect()操作用于执行可变归约。

将这两种方法混合在一起引起的观察 - 你正在改变身份而不是返回一个新的 object 那不是function应该如何减少。

因此,嵌套 Map 的所有条目都将持有对相同值的引用。 那将提供一个MySumObject的实例作为归约的标识。 提醒: reduction reducing()作为U类型的第一个参数标识(不是Supplier ),因此只会创建一个标识object (您可以从构造函数打印一条消息以确保它只会被触发一次)。

为了应用收集器 reduction reducing()来解决这个问题,您需要更改归约逻辑而不是改变身份,归约的每一步都应该产生一个新的MySumObject

为此,我们需要更改 reduction reducing()的第三个参数,即BinaryOperator<U>用于组合MySumObject的两个实例。

MyObject class 中引入映射器方法会更方便。

旁注:尝试使用更有意义的名称。 它使使用代码更容易。 例如, Loan代替MyObjectLoanSum代替MySumObject

这就是它的实现方式:

Map<String, Map<String, MySumObject>> result = list1.stream().collect(
    Collectors.groupingBy(
        MyObject::getLoanType,
        Collectors.groupingBy(
            MyObject::getLoanCurrency,
            Collectors.reducing(
                new MySumObject(),
                MyObject::toMySum,
                MySumObject::merge
            ))
    ));

MySumObject class 与merge()方法返回一个新的 object:

public static class MySumObject {
    private BigDecimal paidSum = BigDecimal.ZERO;
    private BigDecimal remainingSum = BigDecimal.ZERO;
    
    public void addLoan(MyObject loan) {
        paidSum = paidSum.add(loan.getAmountPaid());
        remainingSum = remainingSum.add(loan.getAmountRemaining());
    }
    
    public MySumObject merge(MySumObject other) {
        BigDecimal newPaidSum = paidSum.add(other.getPaidSum());
        BigDecimal newRemainingSum = remainingSum.add(other.getRemainingSum());
        return new MySumObject(newPaidSum, newRemainingSum);
    }
    
    // AllArgs & NoArgs constructors, getters, etc.
}

MyObject class 与映射方法toMySum()

public static class MyObject {
    private String loanType;
    private String loanCurrency;
    private BigDecimal amountPaid;
    private BigDecimal amountRemaining;
    
    public MySumObject toMySum() {
        return new MySumObject(this.amountPaid, this.amountRemaining);
    }
    
    // constructor, getters, etc.
}

在线演示的链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM