文章目录
- 316. Java Stream API - 收集为 Map:使用 `Collectors.toMap()`
- ✨ 基本使用方式:两个函数搞定键和值
- ✅ 示例:构建用户缓存
- ❗️处理重复 Key:传入合并函数
- 🧰 高级用法:指定 Map 实现类
- 🧵 多线程收集:使用 `toConcurrentMap()`
- 📚 总结对比
- 🗣 小结语句(可课堂口播):
316. Java Stream API - 收集为 Map:使用Collectors.toMap()
除了前面讲过的groupingBy()收集器,我们还有一个强大的伙伴:Collectors.toMap(),用于将 Stream 中的元素转换为一个真正的键值对映射(Map)。这一方法非常适合构建缓存、索引或者快速查找的数据结构。
✨ 基本使用方式:两个函数搞定键和值
我们先来看基础语法:
Map<K,V>map=stream.collect(Collectors.toMap(keyMapper,valueMapper));keyMapper:用于生成 Map 的 key。valueMapper:用于生成 Map 的 value。
⚠️ 注意:如果多个元素产生相同的 key,会抛出
IllegalStateException,除非你提供合并逻辑。
✅ 示例:构建用户缓存
假设你有一个User类,每个用户有一个id,你想用id作为 key,把用户缓存起来。
recordUser(longid,Stringname){}List<User>users=List.of(newUser(1L,"Mary"),newUser(2L,"James"),newUser(3L,"Patricia"),newUser(4L,"Michael"));Map<Long,User>userCache=users.stream().collect(Collectors.toMap(User::id,Function.identity()));System.out.println("Map size = "+userCache.size());System.out.println("Keys = "+userCache.keySet());运行结果:
Mapsize=4Keys=[1,2,3,4]User::id生成 keyFunction.identity()返回元素本身作为 value
🎯 用途:常用于缓存、索引表的构建。
❗️处理重复 Key:传入合并函数
如果你预期多个元素会生成相同的 key,你需要提供一个合并函数(BinaryOperator)告诉 Java 如何合并值。
我们来看下面这个示例:
Collection<String>strings=List.of("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve");Map<Integer,String>map=strings.stream().collect(Collectors.toMap(str->str.length(),// key: 字符串长度str->str,// value: 字符串本身(s1,s2)->s1+", "+s2));// 合并函数:拼接字符串map.forEach((key,value)->System.out.println(key+" :: "+value));输出结果:
3::one,two,six,ten4::four,five,nine5::three,seven,eight6::eleven,twelve📌 如果不提供合并函数,会在 key 冲突时抛出异常。
🧰 高级用法:指定 Map 实现类
默认情况下,toMap()返回一个HashMap。但你也可以使用第四个参数传入你想要的 Map 类型:
TreeMap<Integer,String>sortedMap=strings.stream().collect(Collectors.toMap(str->str.length(),str->str,(s1,s2)->s1+", "+s2,TreeMap::new));System.out.println(sortedMap);🧾 输出将是按 key 排序的:
{3=one,two,six,ten,4=four,five,nine,5=three,seven,eight,6=eleven,twelve}🧵 多线程收集:使用toConcurrentMap()
你还可以使用Collectors.toConcurrentMap()来构建线程安全的 Map,适合并行流或多线程场景:
ConcurrentMap<Long,User>userCache=users.parallelStream().collect(Collectors.toConcurrentMap(User::id,Function.identity()));💡 实际类型通常是
ConcurrentHashMap,但规范不保证具体实现。
📚 总结对比
| Collector | 用途 | 支持重复 Key | 下游收集器 | 控制 Map 类型 |
|---|---|---|---|---|
groupingBy() | 分组、统计、分类 | ✅ | ✅ 可选 | ✅ 可选 |
toMap() | 映射、缓存、索引 | ❌(需手动合并) | ❌ | ✅ 可选 |
toConcurrentMap() | 并发安全映射 | ❌(需手动合并) | ❌ | ✅ 默认并发 Map |
🗣 小结语句(可课堂口播):
🔊 “我们用
groupingBy()做统计图、做分类。但当你需要构建键值结构,比如缓存、索引表,就轮到toMap()登场了!要注意 key 冲突的处理,避免运行时炸锅!”