如何总结具有特定 ID 的列表中对象的属性值,并使用 Streams 将其分配给另一个对象

How to Sum up the attribute values of objects in a list having particular IDs and assign it to another object using Streams

我的类.

class MyLoan {
    private Long loanId;
    private BigDecimal loanAmount;
    private BigDecimal totalPaid;
    ....
}
class Customer {
    private Long loanId;
    private List<MyLoan> myLoan;
}

我想从 Customer 迭代 myLoan 并计算 totalPaid 数量。

我的逻辑是“如果 loanId23491L23492L,则添加这两个 loanIdloanAmount 并设置值在 loanId 23490LtotalPaid 数量中。totalPaid 数量 总是显示为零 我的逻辑如下。

并且想使用Java8个流,但是使用流的时候无法写多个条件

BigDecimal spreadAmount;
for (MyLoan myloan: customer.getMyLoan()) {
    if (myloan.getLoanId() == 23491L || myloan.getLoanId() == 23492L) {
        spreadAmount = spreadAmount.add(myloan.getLoanAmount());
    }
    if (myloan.getLoanId() == 23490L) {
        myloan.setTotalPaid(spreadAmount);
    }
}

totalPaid 字段未被修改,因为您的 MyLoan 实例在其他两个 MyLoan 之前遇到了 ID 为 23490l 的实例。

正如@Silvio Mayolo 在评论中建议的那样,您应该首先使用临时变量计算总量,然后将其分配给 ID 为 23490l 的 MyLoan 实例的 totalPaid 字段。

这是您尝试执行的流式实现:

//If to make sure that the element MyLoan invoking the setter is actually present
if (myLoan.stream().map(MyLoan::getLoanId).anyMatch(value -> value == 23490l)){
    myLoan.stream()
            .filter(loan -> loan.getLoanId() == 23490l)
            .findFirst()
            .get()
            .setTotalPaid(myLoan.stream()
                    .filter(loan -> loan.getLoanId() == 23491l || loan.getLoanId() == 23492l)
                    .map(MyLoan::getLoanAmount)
                    .reduce(BigDecimal.valueOf(0), (a, b) -> a = a.add(b)));
}

警告

在使用终端操作 findFirst() 检索到的 Optional 上调用的方法 get() 如果 ID 为 23490l 的 MyLoan 可能会抛出 NoSuchElementException不在列表中。您应该首先确保该元素存在,就像我对 if 语句所做的那样。

第二个(不好的做法)可能涉及捕获 get() 抛出的 NoSuchElementException,以防所需的 MyLoan 不存在。正如评论中指出的那样,捕获 RuntimeExceptionNoSuchElementException 是它的子类)是一种不好的做法,因为我们应该调查问题的根源而不是简单地捕获异常.老实说,第二种方法是(懒惰的)最后的手段,只是为了展示另一种可能的处理方式。

首先,您需要获取要为其定义总支付金额的贷款。如果此步骤成功,则计算总数。

为了使用流找到具有特定 ID 的 loan,您需要在 customers loans 上创建流并申请filter()连同findFirst()就可以了。它将为您提供来自流的 第一个元素,该元素与传递到 过滤器 的谓词相匹配。因为流中可能不存在结果,所以 findFirst() returns 一个 Optional 对象。

可选 class 提供了广泛的交互方法,如 orElse()ifPresent()orElse() 等。避免盲目使用 get() ,除非您没有检查值是否存在,在许多情况下这不是处理它的最方便的方法。就像下面的代码一样,ifPresent() 用于在存在值时继续执行逻辑。

因此,如果找到所需的贷款,下一步就是计算总额。这是通过过滤掉目标 ID,通过应用 map() 提取金额并使用 reduce() 作为终端操作将金额加在一起来完成的。

public static void setTotalPaid(Customer customer, Long idToSet, Long... idsToSumUp) {
    List<MyLoan> loans = customer.getMyLoan();
    getLoanById(loans, idToSet).ifPresent(loan -> loan.setTotalPaid(getTotalPaid(loans, idsToSumUp)));
}

public static Optional<MyLoan> getLoanById(List<MyLoan> loans, Long id) {
    return loans.stream()
        .filter(loan -> loan.getLoanId().equals(id))
        .findFirst();
}

public static BigDecimal getTotalPaid(List<MyLoan> loans, Long... ids) {
    Set<Long> targetLoans = Set.of(ids); // wrapping with set to improve performance
    
    return loans.stream()
        .filter(loan -> targetLoans.contains(loan.getLoanId()))
        .map(MyLoan::getLoanAmount)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
}