Java 中使用 Stream 将 List 转换为 Map 实战笔记(生产级版)
在日常开发中,将 List
转换为 Map
是高频操作(如通过 ID 快速查询对象、按属性分组等)。使用 Stream API 的 Collectors.toMap()
可以优雅实现,但生产环境中需关注键冲突、null 键、Map 实现选择等细节。本文系统梳理核心用法、易错点及最佳实践,帮你写出健壮且高效的代码。
一、基础准备:前提与场景
1. 示例对象定义
下文示例基于 User
类,含常见属性及 getter 方法(生产中建议用 Lombok 简化,@Data
注解会自动生成 getter、equals、hashCode 等方法,减少模板代码):
@Data // Lombok 注解:自动生成 getter/setter/equals/hashCode/toString
public class User {
private Integer id; // 包装类型,可能为 null(需特别处理)
private String name; // 可能为 null
private int age; // 基本类型,无 null 问题
public User(Integer id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
2. 核心方法:Collectors.toMap()
签名解析
toMap()
有两个重载方法,需根据场景选择:
// 基础版:仅指定键、值映射(默认用 HashMap,键冲突会抛异常)
toMap(
Function<? super T, ? extends K> keyMapper, // 键映射逻辑
Function<? super T, ? extends U> valueMapper // 值映射逻辑
)
// 完整版:支持键冲突处理 + 指定 Map 实现(生产级场景首选)
toMap(
Function<? super T, ? extends K> keyMapper, // 键映射逻辑
Function<? super T, ? extends U> valueMapper, // 值映射逻辑
BinaryOperator<U> mergeFunction, // 键冲突处理策略
Supplier<M> mapFactory // 指定 Map 实现(如 TreeMap)
)
✨ 核心区别:基础版仅适用于「键绝对唯一且无需定制 Map 类型」的场景,生产中更推荐完整版,避免隐藏风险。
二、基础转换:方法引用 vs Lambda 表达式
1. 简单属性映射(最常用场景)
根据映射逻辑的复杂度,可选择更简洁的方法引用或更灵活的 Lambda:
方法引用
适用场景: 映射逻辑简单(直接取属性),代码更简洁
Map<Integer, String> userMapByMethodRef = users.stream() .collect(Collectors.toMap(User::getId,