2

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.

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46

5 Answers5

3

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.

otoomey
  • 939
  • 1
  • 14
  • 31
2
Map<String, BillingAccount> billingAccountsMap = billingAccounts.stream()
        .filter(ba -> ba.getBillingAccountIdentifier() != null
                && ba.getBillingAccountIdentifier().getIdentifier() != null)
        .collect(Collectors.toMap(ba -> ba.getBillingAccountIdentifier().getIdentifier(), ba -> ba));
Dusty
  • 21
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 14 '22 at 04:41
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.

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
1

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()));
Dan
  • 3,647
  • 5
  • 20
  • 26
1

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()));
Ashish Patil
  • 4,428
  • 1
  • 15
  • 36