背景
PS:本文源于真实开发场景,对应的理论讲解在此教程:Java 8 Stream API 在数据转换中的应用 —— 将列表转换为映射。
在日常的开发过程中,我们经常需要将一个包含多个对象的列表转换成一个映射(Map),以便于快速查找和访问数据。在Java 8之前,这样的转换通常需要通过循环等传统方式手动实现,这不仅增加了代码量,还可能引入更多的错误。而Java 8引入的Stream API提供了一种更加简洁、优雅的方式来完成这类任务。
基础概念
- Stream API:是Java 8新增的一个强大的功能,它可以让你以一种声明式的方式处理数据集合。Stream API可以显著提高Java程序员在处理集合框架时的效率。
- Map:是一个存储键值对的数据结构,允许通过键快速检索值。
- Collectors.toMap():是Java 8 Stream API中的一个收集器,用于将流中的元素收集到Map中。
示例代码解析
假设我们有一个sysUserList
列表,它包含了多个SysUser
对象,每个对象都有一个唯一的userId
属性。我们的目标是将这个列表转换成一个Map,其中userId
作为键,SysUser
对象作为值。
if (CollectionUtil.isNotEmpty(sysUserList)) {
Map<Long, SysUser> sysUserMap = sysUserList
.stream()
.collect(Collectors.toMap(SysUser::getUserId, a -> a, (k1, k2) -> k1));
for (CourseVo dto : courseList) {
if (sysUserMap.get((long) dto.getCreateUser()) != null) {
dto.setCreateUserName(sysUserMap.get((long) dto.getCreateUser()).getUserName());
}
}
}
代码详解
-
条件判断:首先检查
sysUserList
是否非空,这是为了避免对空列表进行操作导致的潜在错误。 -
转换过程:通过调用
sysUserList.stream()
方法将列表转换为一个流,然后使用collect
方法配合Collectors.toMap
将流中的元素收集到一个Map中。SysUser::getUserId
:指定了Map的键,即SysUser
对象的userId
属性。a -> a
:指定了Map的值,即SysUser
对象本身。(k1, k2) -> k1
:这是一个合并函数,用于解决键冲突的问题。当流中有多个元素的userId
相同时,此函数会决定如何处理这些冲突。在这个例子中,选择保留第一个遇到的对象(k1
)并忽略后续相同键的对象(k2
)。
-
利用Map更新课程信息:遍历
courseList
,根据每个CourseVo
对象的创建者ID(createUser
)从sysUserMap
中查找对应的SysUser
对象,并设置CourseVo
对象的createUserName
属性。
键冲突处理的深入理解
在实际应用中,键冲突是一个常见的问题。例如,如果sysUserList
中存在多个SysUser
对象具有相同的userId
,那么在构建Map时就需要指定如何处理这种情况。在上面的例子中,我们选择了保留第一次遇到的对象并忽略后续的对象。但是,根据不同的业务需求,我们也可能选择其他策略,比如:
- 合并对象:可以将所有具有相同键的对象的信息合并到一个新对象中。
- 选择最新的对象:可以基于某个属性(如创建时间)选择最新的对象。
- 抛出异常:如果键冲突是不可接受的,可以选择抛出异常。
结论
通过使用Java 8的Stream API,我们可以更高效、更简洁地完成数据转换的任务。Collectors.toMap
提供了强大的功能来帮助我们构建映射,同时通过自定义合并函数,我们能够灵活地处理键冲突的情况。这种做法不仅提高了代码的可读性和可维护性,还减少了出错的可能性。