Java Stream API 实战:如何将 List 转换为 Map 并处理重复键值
在日常开发中,我们经常需要将一个 List 转换为 Map,并且在某些情况下使用 List 中的某个字段作为 Map 的键。Java Stream API 提供了强大的工具来完成这项任务,但在遇到重复键或空键值时,往往会遇到一些棘手的问题。本文将以一个具体案例为例,展示如何优雅地将 List 转换为 Map,并处理键冲突、空键等情况。
案例背景
假设我们有一个包含设备信息的 List<Map<String, Object>> 数据结构,每个设备有 id 和 name 两个字段,我们希望将 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);
可以看到,数据中包含以下几种特殊情况:
id值重复(110出现了两次)。id值为null。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 // 键重复时保留已有的值
));
关键点详解
-
过滤空值:使用
filter过滤掉null对象以及id为空或null的项。我们假设StringUtil.isNotBlank和Util.getStrOfObj方法可以检查字符串是否为空。java 复制代码 .filter(item -> StringUtil.isNotBlank(Util.getStrOfObj(item.get("id")))) -
指定键与值:
Collectors.toMap的第一个参数指定了Map的键,这里我们选择id字段。第二个参数指定了Map的值,直接将item本身作为值存入Map。 -
处理键冲突:如果遇到重复的
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 会显著提升编码效率。