news 2026/4/3 5:16:20

使用Java Stream,将集合转换为一对一Map

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用Java Stream,将集合转换为一对一Map

在日常的开发工作中,我们经常使用到Java Stream,特别是Stream API中提供的Collectors.toList()收集器,

但有些场景下,我们需要将集合转换为Map,这时候就需要使用到Stream API中提供的另一个收集器:

Collectors.toMap,它可以将流中的元素映射为键值对,并收集到一个Map中。

1. 三种主要的重载方法

Collectors.toMap有3种重载方法,分别是:

1)两个参数的重载方法(最简单的形式)

/* by 01130.hk - online tools website : 01130.hk/zh/linuxcmd.html */ public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new); }

2)三个参数的重载方法(包含冲突处理)

/* by 01130.hk - online tools website : 01130.hk/zh/linuxcmd.html */ public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) { return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); }

3)四个参数的重载方法(指定Map实现)

public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier) { BiConsumer<M, T> accumulator = (map, element) -> map.merge(keyMapper.apply(element), valueMapper.apply(element), mergeFunction); return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID); }

接下来,我们结合使用示例详细讲解。

2. 使用示例

2.1 将对象的某些属性转换为Map

假设有一个城市列表,需要将其转换为Map,其中Key为城市ID、Value为城市名称,转换方法如下所示:

@Getter @Setter public class City { private Integer cityId; private String cityName; public City(Integer cityId, String cityName) { this.cityId = cityId; this.cityName = cityName; } }
List<City> cityList = Arrays.asList( new City(1, "北京"), new City(2, "上海"), new City(3, "广州"), new City(4, "深圳") ); Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName)); System.out.println(cityMap);

输出结果:

2.2 将对象列表转换为Map(ID -> 对象)

仍然使用上面的城市列表,需要将其转换为Map,其中Key为城市ID、Value为城市对象,转换方法如下所示:

List<City> cityList = Arrays.asList( new City(1, "北京"), new City(2, "上海"), new City(3, "广州"), new City(4, "深圳") ); Map<Integer, City> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, city -> city)); City city = cityMap.get(1); System.out.println("城市ID: " + city.getCityId()); System.out.println("城市名称: " + city.getCityName());

输出结果如下所示:

城市ID: 1
城市名称: 北京

上面的写法等价于:

Map<Integer, City> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, Function.identity()));

因为Function.identity()内部实现是下面这样的:

static <T> Function<T, T> identity() { return t -> t; }

2.3 键冲突处理

假设上面的城市列表中有一个ID重复的城市:

List<City> cityList = Arrays.asList( new City(1, "北京"), new City(2, "上海"), new City(3, "广州"), new City(4, "深圳"), new City(4, "天津") ); Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName)); System.out.println("城市ID: 4, 城市名称: " + cityMap.get(4));

此时运行代码,会抛出java.lang.IllegalStateException异常,如下图所示:

有3种常见的键冲突处理方式,分别是保留旧值、使用新值和合并值,接下来一一讲解。

1)方式一:保留旧值

Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName, (oldValue, newValue) -> oldValue));

输出结果:

城市ID: 4, 城市名称: 深圳

2)方式二:使用新值

Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName, (oldValue, newValue) -> newValue));

输出结果:

城市ID: 4, 城市名称: 天津

3)方式三:合并值

Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName, (oldValue, newValue) -> oldValue + ", " + newValue));

输出结果:

城市ID: 4, 城市名称: 深圳, 天津

2.4 数据分组聚合

假设有一个销售记录列表,需要将其转换为Map,其中Key为销售员、Value为该销售员的总销售额,转换方法如下所示:

@Getter @Setter public class SalesRecord { private String salesPerson; private BigDecimal amount; public SalesRecord(String salesPerson, BigDecimal amount) { this.salesPerson = salesPerson; this.amount = amount; } }
List<SalesRecord> salesRecordList = Arrays.asList( new SalesRecord("张三", new BigDecimal("1000")), new SalesRecord("李四", new BigDecimal("2000")), new SalesRecord("张三", new BigDecimal("980")) ); Map<String, BigDecimal> salesRecordMap = salesRecordList.stream() .collect(Collectors.toMap(SalesRecord::getSalesPerson, SalesRecord::getAmount, BigDecimal::add)); System.out.println(salesRecordMap);

输出结果:

上面的例子是销售额累加,也可以只取最小值:

Map<String, BigDecimal> salesRecordMap = salesRecordList.stream() .collect(Collectors.toMap(SalesRecord::getSalesPerson, SalesRecord::getAmount, BigDecimal::min));

此时的输出结果:

或者只取最大值:

Map<String, BigDecimal> salesRecordMap = salesRecordList.stream() .collect(Collectors.toMap(SalesRecord::getSalesPerson, SalesRecord::getAmount, BigDecimal::max));

此时的输出结果:

2.5 指定Map实现

默认情况下,Collectors.toMap是将结果收集到HashMap中,如果有需要,我们也可以指定成TreeMap或者LinkedHashMap。

如果想要保持插入顺序,可以指定使用LinkedHashMap:

List<City> cityList = Arrays.asList( new City(2, "上海"), new City(1, "北京"), new City(4, "深圳"), new City(3, "广州") ); Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName, (existing, replacement) -> existing, LinkedHashMap::new)); System.out.println(cityMap);

输出结果:

如果想要按键排序,可以指定使用TreeMap:

List<City> cityList = Arrays.asList( new City(2, "上海"), new City(1, "北京"), new City(4, "深圳"), new City(3, "广州") ); Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName, (existing, replacement) -> existing, TreeMap::new)); System.out.println(cityMap);

输出结果:

3. 注意事项

3.1 空异常

如果valueMapper中取出的值有null值,会抛出java.lang.NullPointerException异常,如下示例:

List<City> cityList = Arrays.asList( new City(1, "北京"), new City(2, "上海"), new City(3, "广州"), new City(4, "深圳"), new City(5, null) ); Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName)); System.out.println(cityMap);

运行以上代码会抛出异常,如下图所示:

有两种解决方案,第一种解决方案是过滤null值:

Map<Integer, String> cityMap = cityList.stream() .filter(city -> city.getCityName() != null) .collect(Collectors.toMap(City::getCityId, City::getCityName));

第二种解决方案是提供默认值:

Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, city -> Optional.ofNullable(city.getCityName()).orElse("未知")));

3.2 键重复异常

如果出现重复键,且没有提供mergeFunction参数,会抛出java.lang.IllegalStateException异常,如下示例:

List<City> cityList = Arrays.asList( new City(1, "北京"), new City(2, "上海"), new City(3, "广州"), new City(4, "深圳"), new City(4, "天津") ); Map<Integer, String> cityMap = cityList.stream() .collect(Collectors.toMap(City::getCityId, City::getCityName)); System.out.println(cityMap);

运行以上代码会抛出异常,如下图所示:

解决方案见本篇文章2.3 键冲突处理部分。

4. 总结

Collectors.toMap是Stream API中提供的一个非常方便的收集器,它可以将流中的元素映射为键值对,并收集到一个Map中。

它适用于一对一映射的场景,但在使用时,要注意避免java.lang.NullPointerException异常和

java.lang.IllegalStateException异常。

文章持续更新,欢迎关注微信公众号「申城异乡人」第一时间阅读!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/30 10:14:57

AI生图模型系统开发:从“画得像”到“画得准”的技术攻坚战

你可能用过AI绘画工具&#xff1a;输入“一只穿着宇航服的熊猫在火星种竹子”&#xff0c;几秒后一张逼真图片就出来了。但背后支撑这一切的&#xff0c;是一整套复杂而精密的AI生图模型系统。它不是调用一个现成API那么简单&#xff0c;而是一场融合算法、工程、数据与产品思维…

作者头像 李华
网站建设 2026/3/15 10:59:46

5分钟精通pot-desktop:从此图片文字一键提取[特殊字符]

还记得上周整理资料时的崩溃瞬间吗&#xff1f;PDF里的重要数据无法复制&#xff0c;只能逐字敲打&#xff1b;截图中的关键信息&#xff0c;只能手动输入&#xff1b;网课课件里的重点内容&#xff0c;想要摘录却无从下手。这些看似简单的任务&#xff0c;却耗费了我们大量的时…

作者头像 李华
网站建设 2026/3/28 16:26:06

OpCore Simplify:重新定义Hackintosh配置体验的智能引擎

OpCore Simplify&#xff1a;重新定义Hackintosh配置体验的智能引擎 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 在追求完美Hackintosh配置的道路上…

作者头像 李华
网站建设 2026/3/16 2:58:15

PHP基础怎么学?这些核心、错误点你得知道

在众多编程语言中&#xff0c;PHP以其在Web开发领域的广泛应用而闻名。它是一种服务器端脚本语言&#xff0c;尤其适合构建动态网站。对于初学者而言&#xff0c;PHP的语法相对直观&#xff0c;学习曲线较为平缓&#xff0c;这使其成为许多开发者进入后端世界的第一站。然而&am…

作者头像 李华
网站建设 2026/3/16 5:18:08

3个隐藏技巧实现系统性能跃迁:AtlasOS终极优化方案

AtlasOS作为专为性能、隐私和安全优化的Windows轻量级修改版&#xff0c;通过智能驱动配置和系统调优工具链&#xff0c;为游戏玩家和专业用户提供了前所未有的性能提升体验。 【免费下载链接】Atlas &#x1f680; An open and lightweight modification to Windows, designed…

作者头像 李华
网站建设 2026/3/31 7:39:09

Steamless完全指南:轻松移除Steam游戏DRM限制

Steamless是一款专业的开源工具&#xff0c;专门用于移除Steam游戏中的SteamStub DRM保护。无论你是想要在离线环境下畅玩游戏的普通用户&#xff0c;还是对DRM技术感兴趣的技术爱好者&#xff0c;这款工具都能为你提供完美的解决方案。 【免费下载链接】Steamless Steamless i…

作者头像 李华