Home > database >  Rewrite imperative code of creating and populating Map in a declarative way using Java Stream API
Rewrite imperative code of creating and populating Map in a declarative way using Java Stream API

Time:05-14

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. Thanks in advance!

CodePudding user response:

Use Collectors.toMap(..) to convert a stream of items to a map. 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.

CodePudding user response:

Map<String, BillingAccount> billingAccountsMap = billingAccounts.stream()
        .filter(ba -> ba.getBillingAccountIdentifier() != null
                && ba.getBillingAccountIdentifier().getIdentifier() != null)
        .collect(Collectors.toMap(ba -> ba.getBillingAccountIdentifier().getIdentifier(), ba -> ba));

CodePudding user response:

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()));

CodePudding user response:

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.

CodePudding user response:

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()));
  • Related