使用 Java Stream API 以声明方式重写创建和填充 Map 的命令式代码

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

我有这段代码:

Map<String, BillingAccount> billingAccountsMap = new HashMap<>();
for (BillingAccount ba : billingAccounts) {
    if (ba.getBillingAccountIdentifier() != null && ba.getBillingAccountIdentifier().getIdentifier() != null) {
        billingAccountsMap.put(ba.getBillingAccountIdentifier().getIdentifier(), ba);
    }
}

我只想用 Java Stream API 和 collect(Collectors.toMap()) 以功能方式重写它,但我对 null 情况有点困惑。

我正在使用 Java 11.

您可以使用 filter() operation and then apply collect by passing a built-in collector Collectors.toMap() 过滤掉可空值:

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

请注意,对于这种方法,每个标识符都必须是唯一的。否则,您需要提供 mergeFunction 作为第三个参数来解析映射到同一键的值。

使用 Collectors.toMap(..) 将项目流转换为地图,并使用 filter() 删除不需要的项目。在你的情况下:

var billingAccountsMap = billingAccounts.stream()
        .filter(ba -> ba.getBillingAccountIdentifier() != null)
        .filter(ba -> ba.getBillingAccountIdentifier().getIdentifier() != null)
        .collect(Collectors.toMap(ba -> ba.getBillingAccountIdentifier().getIdentifier(), ba -> ba));

查看此answer了解更多信息。

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

您可以流式传输 BillingAccountList,然后使用聚合操作 filter 确保每个 BillingAccount 及其嵌套字段不为空short-circuited 条件。最后,收集结果,其中每个键是 BillingAccount 的标识符,而对应的值是对象本身。

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

您可以使用 groupingBy 一旦 filter 应用空检查:

billingAccounts.stream()
        .filter(ba -> ba.getBillingAccountIdentifier() != null
                        && ba.getBillingAccountIdentifier().getIdentifier() != null)
                            .collect(Collectors.groupingBy(x -> x.getBillingAccountIdentifier().getIdentifier()));