My classes.
class MyLoan {
private Long loanId;
private BigDecimal loanAmount;
private BigDecimal totalPaid;
....
}
class Customer {
private Long loanId;
private List<MyLoan> myLoan;
}
I want to iterate over the myLoan
from a Customer
and calculate the totalPaid
amount.
My logic is "If loanId
is 23491L
or 23492L
, then add the loanAmount
of those two loanId
's and set the value in the totalPaid
amount of loanId
23490L
".totalPaid
amount is always showing as zero with my logic below.
And want to use Java 8 streams, but unable to write multiple conditions when using streams.
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);
}
}
CodePudding user response:
The totalPaid
field is not modified because your MyLoan
instance with id 23490l is encountered before the other two MyLoan
s.
As @Silvio Mayolo has suggested in the comments you should first compute the total amount with a temp variable and then assign it to the totalPaid
field of the MyLoan
instance with id 23490l.
This is a stream implementation of what you were trying to do:
//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)));
}
WARNING
The aggregate operation get()
could throw a NoSuchElementException
if a MyLoan
with id 23490l is not present within the list. This code must be used carefully by making sure that such element is present or at least handled in a try-catch block.
CodePudding user response:
Firstly, you need to fetch a loan for which you want to define a total paid amount. If this step succeeds, then calculate a total.
In order to find a loan with a particular id using streams, you need to create a stream over the customers loans and apply filter()
in conjunction with findFirst()
on it. It'll give you the first element from the stream that matches the predicate passed into the filter. Because result might not be present in the stream, findFirst()
returns an Optional
object.
Optional class offers a wide range of method to interact with it like orElse()
, ifPresent()
, orElse()
, etc. Avoid blindly using get()
, unless you didn't check that value is present, which is in many cases isn't the most convenient way to deal with it. Like in the code below, ifPresent()
is being used to proceed with the logic if value is present.
So if the required loan was found, the next step is to calculate the total. Which is done by filtering out target ids, extracting amount by applying map()
and adding the amounts together using reduce()
as a terminal operation.
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);
}