一、基础用法:Collectors.toMap()
1. 基本语法
Map<K, V> map = list.stream().collect(Collectors.toMap(
元素 -> key, // key映射函数
元素 -> value // value映射函数
));
2. 简单示例:List<Person> → Map<Long, String>
// 准备数据
List<Person> people = Arrays.asList(
new Person(1L, "张三", 25),
new Person(2L, "李四", 30),
new Person(3L, "王五", 28)
);
// 转Map:id为key,name为value
Map<Long, String> idNameMap = people.stream()
.collect(Collectors.toMap(
Person::getId, // key: Person的id
Person::getName // value: Person的name
));
// 结果:{1=张三, 2=李四, 3=王五}
3. 元素本身作为 value
// id为key,Person对象本身为value
Map<Long, Person> idPersonMap = people.stream()
.collect(Collectors.toMap(
Person::getId,
person -> person // 或 Function.identity()
));
二、处理重复 key:提供合并策略
当 List 中有重复 key 时,必须指定冲突解决方案,否则会抛 IllegalStateException。
List<Person> people = Arrays.asList(
new Person(1L, "张三", 25),
new Person(1L, "张三丰", 30), // key重复
new Person(2L, "李四", 28)
);
// 保留后者
Map<Long, String> map1 = people.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(oldValue, newValue) -> newValue // 冲突时取新值
));
// 保留前者
Map<Long, String> map2 = people.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(oldValue, newValue) -> oldValue // 冲突时取旧值
));
// 拼接
Map<Long, String> map3 = people.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(v1, v2) -> v1 + "," + v2 // 拼接:张三,张三丰
));
三、指定 Map 类型
默认返回 HashMap,可指定返回 LinkedHashMap 或 TreeMap。
// 返回 LinkedHashMap(保持插入顺序)
Map<Long, String> linkedMap = people.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(v1, v2) -> v2, // 合并策略
LinkedHashMap::new // 指定Map类型
));
// 返回 TreeMap(按key排序)
Map<Long, String> treeMap = people.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(v1, v2) -> v2,
TreeMap::new
));
四、Collectors.groupingBy():一对多分组
当需要将 List 转为 Map<K, List<V>> 时使用。
List<Person> people = Arrays.asList(
new Person(1L, "张三", 25),
new Person(2L, "李四", 25),
new Person(3L, "王五", 30),
new Person(4L, "赵六", 30)
);
// 1. 按年龄分组:Map<Integer, List<Person>>
Map<Integer, List<Person>> ageGroup = people.stream()
.collect(Collectors.groupingBy(Person::getAge));
// 结果:{
// 25=[Person(1,张三,25), Person(2,李四,25)],
// 30=[Person(3,王五,30), Person(4,赵六,30)]
// }
// 2. 分组后提取某个字段:Map<Integer, List<String>>
Map<Integer, List<String>> ageNames = people.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.mapping(Person::getName, Collectors.toList())
));
// 结果:{25=[张三, 李四], 30=[王五, 赵六]}
// 3. 分组后计数:Map<Integer, Long>
Map<Integer, Long> ageCount = people.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.counting()
));
// 结果:{25=2, 30=2}
// 4. 指定返回 LinkedHashMap(保持key的顺序)
Map<Integer, List<Person>> orderedGroup = people.stream()
.collect(Collectors.groupingBy(
Person::getAge,
LinkedHashMap::new,
Collectors.toList()
));
五、常见异常及解决
| 异常 | 原因 | 解决 |
|---|---|---|
IllegalStateException: Duplicate key | key 重复且未指定合并策略 | 添加第三个参数 (v1,v2)->v2 |
NullPointerException | value 为 null | 改用 collect(HashMap::new, (m,v)->m.put(k,v), ...) 或过滤 null |
| 类型推断失败 | Lambda 表达式模糊 | 显式声明参数类型或方法引用 |
// 处理 value 为 null 的情况
Map<Long, String> map = people.stream()
.collect(HashMap::new,
(m, v) -> m.put(v.getId(), v.getName()),
HashMap::putAll);
六、方法速查表
| 需求 | 方法 | 示例 |
|---|---|---|
| List → Map(1:1) | toMap(key, value) | toMap(Person::getId, Person::getName) |
| List → Map(有重复key) | toMap(key, value, merge) | toMap(Person::getId, Person::getName, (v1,v2)->v2) |
| List → Map(指定类型) | toMap(key, value, merge, mapType) | toMap(..., LinkedHashMap::new) |
| List → 分组Map(1:N) | groupingBy(classifier) | groupingBy(Person::getAge) |
| 分组+聚合 | groupingBy(classifier, downstream) | groupingBy(Person::getAge, counting()) |
掌握这些,Java Stream 的 List 转 Map 操作就基本没问题了!

1878

被折叠的 条评论
为什么被折叠?



