'Rewrite imperative code of creating and populating Map in a declarative way using Java Stream API
I have this piece of code :
Map<String, BillingAccount> billingAccountsMap = new HashMap<>();
for (BillingAccount ba : billingAccounts) {
if (ba.getBillingAccountIdentifier() != null && ba.getBillingAccountIdentifier().getIdentifier() != null) {
billingAccountsMap.put(ba.getBillingAccountIdentifier().getIdentifier(), ba);
}
}
All I want is to rewrite it in a functional way with Java Stream API and collect(Collectors.toMap())
, but I am a bit perplexed with the null
cases.
I am using Java 11.
Solution 1:[1]
You can filter out nullable values using filter()
operation and then apply collect
by passing a built-in collector Collectors.toMap()
into it:
Map<String, BillingAccount> billingAccountsById = billingAccounts.stream()
.filter(account -> Objects.nonNull(account.getBillingAccountIdentifier()))
.filter(account -> Objects.nonNull(account.getBillingAccountIdentifier().getIdentifier()))
.collect(Collectors.toMap(
account -> account.getBillingAccountIdentifier().getIdentifier(), // keyMapper
Function.identity())); // valueMapper
Note that for this approach, every identifier has to be unique. Otherwise, you need to provide mergeFunction
as the third argument to resolve values mapped to the same key.
Solution 2:[2]
Use Collectors.toMap(..)
to convert a stream of items to a map, and filter()
to remove items you don't want. In your case:
var billingAccountsMap = billingAccounts.stream()
.filter(ba -> ba.getBillingAccountIdentifier() != null)
.filter(ba -> ba.getBillingAccountIdentifier().getIdentifier() != null)
.collect(Collectors.toMap(ba -> ba.getBillingAccountIdentifier().getIdentifier(), ba -> ba));
See this answer for more information.
Solution 3:[3]
Map<String, BillingAccount> billingAccountsMap = billingAccounts.stream()
.filter(ba -> ba.getBillingAccountIdentifier() != null
&& ba.getBillingAccountIdentifier().getIdentifier() != null)
.collect(Collectors.toMap(ba -> ba.getBillingAccountIdentifier().getIdentifier(), ba -> ba));
Solution 4:[4]
You could stream your List
of BillingAccount
, then use the aggregate operation filter
to make sure that each BillingAccount
and its nested fields are not null with a short-circuited condition. Finally, collect the results where each key is the BillingAccount
's identifier while the corresponding value is the object itself.
Map<String, BillingAccount> billingAccountsMap = billingAccounts.stream()
.filter(ba -> Objects.nonNull(ba)
&& Objects.nonNull(ba.getBillingAccountIdentifier())
&& Objects.nonNull(ba.getBillingAccountIdentifier().getIdentifier()))
.collect(Collectors.toMap(ba -> ba.getBillingAccountIdentifier().getIdentifier(), Function.identity()));
Solution 5:[5]
You can use groupingBy
once filter
for null checks got applied:
billingAccounts.stream()
.filter(ba -> ba.getBillingAccountIdentifier() != null
&& ba.getBillingAccountIdentifier().getIdentifier() != null)
.collect(Collectors.groupingBy(x -> x.getBillingAccountIdentifier().getIdentifier()));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | Dusty |
Solution 4 | |
Solution 5 | Ashish Patil |