Java Stream API 实战:如何将 List 转换为 Map 并处理重复键值

1,747 阅读3分钟

Java Stream API 实战:如何将 List 转换为 Map 并处理重复键值

在日常开发中,我们经常需要将一个 List 转换为 Map,并且在某些情况下使用 List 中的某个字段作为 Map 的键。Java Stream API 提供了强大的工具来完成这项任务,但在遇到重复键或空键值时,往往会遇到一些棘手的问题。本文将以一个具体案例为例,展示如何优雅地将 List 转换为 Map,并处理键冲突、空键等情况。

案例背景

假设我们有一个包含设备信息的 List<Map<String, Object>> 数据结构,每个设备有 idname 两个字段,我们希望将 id 字段作为键,整个 Map 作为值,转换为 Map<String, Map<String, Object>> 的结构。

测试数据如下:

List<Map<String, Object>> list = new ArrayList<>();

// 测试数据初始化
Map<String, Object> item1 = new HashMap<>();
item1.put("id", "110");
item1.put("name", "詹三");

Map<String, Object> item2 = new HashMap<>();
item2.put("id", "220");
item2.put("name", "杨武");

Map<String, Object> item3 = new HashMap<>();
item3.put("id", "110"); // 重复的键测试
item3.put("name", "花海");

Map<String, Object> item4 = new HashMap<>();
item4.put("id", null); // null 值测试
item4.put("name", "非请");

Map<String, Object> item5 = new HashMap<>();
item5.put("id", ""); // 空字符串测试
item5.put("name", "院士");

list.add(item1);
list.add(item2);
list.add(item3);
list.add(item4);
list.add(item5);

可以看到,数据中包含以下几种特殊情况:

  1. id 值重复(110 出现了两次)。
  2. id 值为 null
  3. id 值为空字符串。

使用 Stream API 转换并处理重复键

在将 List 转换为 Map 的过程中,我们使用 Collectors.toMap,并添加对 id 的非空判断,同时指定了键冲突时的处理逻辑。

Map<String, Map<String, Object>> map = list.stream()
        .filter(Objects::nonNull) // 过滤掉 null 的对象
        .filter(item -> StringUtil.isNotBlank(Util.getStrOfObj(item.get("id")))) // 过滤掉 null 或空字符串的 id
        .collect(Collectors.toMap(
                item -> Util.getStrOfObj(item.get("id")), // 使用 id 作为 Map 的键
                item -> item,                             // 值为整个 Map 本身
                (existing, replacement) -> existing       // 键重复时保留已有的值
        ));

关键点详解

  1. 过滤空值:使用 filter 过滤掉 null 对象以及 id 为空或 null 的项。我们假设 StringUtil.isNotBlankUtil.getStrOfObj 方法可以检查字符串是否为空。

    java
    复制代码
    .filter(item -> StringUtil.isNotBlank(Util.getStrOfObj(item.get("id"))))
    
  2. 指定键与值Collectors.toMap 的第一个参数指定了 Map 的键,这里我们选择 id 字段。第二个参数指定了 Map 的值,直接将 item 本身作为值存入 Map

  3. 处理键冲突:如果遇到重复的 id,即键冲突的情况,使用 (existing, replacement) -> existing 选择保留已有的 Map 值,不覆盖。

结果展示

我们使用 forEach 方法打印出 Map 的内容:

map.forEach((key, value) -> System.out.printf("%s : %s \n", key, value));

执行后,输出结果如下:

110 : {id=110, name=詹三}
220 : {id=220, name=杨武}

可以看到:

  • 110 的重复键问题已经解决,保留了第一个 item1 的值。
  • null 和空字符串的 id 已被过滤,不会出现在结果中。

总结

通过 Java Stream API 提供的 Collectors.toMap 方法,我们可以高效地将 List 转换为 Map。在处理过程中,通过合理使用 filter、自定义键冲突解决策略,可以优雅地避免空值和重复键的问题。

附加代码

以下是完整的代码示例,供参考:

import java.util.*;
import java.util.stream.Collectors;

public class ListToMapExample {

    public static void main(String[] args) {
        List<Map<String, Object>> list = new ArrayList<>();

        // 添加测试数据
        Map<String, Object> item1 = new HashMap<>();
        item1.put("id", "110");
        item1.put("name", "詹三");

        Map<String, Object> item2 = new HashMap<>();
        item2.put("id", "220");
        item2.put("name", "杨武");

        Map<String, Object> item3 = new HashMap<>();
        item3.put("id", "110"); // 重复的键测试
        item3.put("name", "花海");

        Map<String, Object> item4 = new HashMap<>();
        item4.put("id", null); // null 值测试
        item4.put("name", "非请");

        Map<String, Object> item5 = new HashMap<>();
        item5.put("id", ""); // 空字符串测试
        item5.put("name", "院士");

        list.add(item1);
        list.add(item2);
        list.add(item3);
        list.add(item4);
        list.add(item5);

        // list转map
        Map<String, Map<String, Object>> map = list.stream()
                .filter(Objects::nonNull)
                .filter(item -> StringUtil.isNotBlank(Util.getStrOfObj(item.get("id"))))
                .collect(Collectors.toMap(
                        item -> Util.getStrOfObj(item.get("id")),
                        item -> item,
                        (existing, replacement) -> existing
                ));

        // 打印结果
        map.forEach((key, value) -> System.out.printf("%s : %s \n", key, value));
    }
}

使用 Stream 简化数据转换的同时处理特殊情况,提高代码健壮性和可读性,是 Java 8 带来的开发利器。在未来的开发中,善用 Stream API 会显著提升编码效率。