
本文深入探讨了如何利用J*a Stream API中的`Collectors.toMap`方法,高效地将数据流转换为Map。核心内容是演示如何在键冲突时,通过自定义合并函数对BigDecimal类型的值进行累加求和,并强调了使用`HashMap::new`作为Map工厂的正确实践,以确保代码的简洁性和封装性,避免外部Map的预先创建。
在J*a开发中,将一个对象集合转换为Map是一种常见操作。尤其当转换过程中可能出现键冲突,并且需要对冲突键的值进行聚合(例如求和)时,J*a Stream API提供了强大而灵活的解决方案。本文将详细介绍如何使用Collectors.toMap结合自定义合并函数和Map工厂,实现对Map中现有键的值进行累加求和。
假设我们有一个Position对象的列表,每个Position对象包含assetId、currencyId和value(BigDecimal类型)。我们需要将这些Position对象转换为一个Map
为了清晰地演示,我们首先定义相关的辅助类:
import j*a.math.BigDecimal;
import j*a.util.Objects;
import j*a.util.List;
import j*a.util.ArrayList;
import j*a.util.Map;
import j*a.util.HashMap;
import j*a.util.stream.Collectors;
// 组合键类
class PositionKey {
String assetId;
String currencyId;
public PositionKey(String assetId, String currencyId) {
this.assetId = assetId;
this.currencyId = currencyId;
}
// 必须重写equals和hashCode,确保Map的键能够正确比较
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PositionKey that = (PositionKey) o;
return Objects.equals(assetId, that.assetId) &&
Objects.equals(currencyId, that.currencyId);
}
@Override
public int hashCode() {
return Objects.hash(assetId, currencyId);
}
@Override
public String toString() {
return "PositionKey{" +
"assetId='" + assetId + '\'' +
", currencyId='" + currencyId + '\'' +
'}';
}
}
// 持仓对象类
class Position {
String assetId;
String currencyId;
BigDecimal value;
public Position(String assetId, String currencyId, BigDecimal value) {
this.assetId = assetId;
this.currencyId = currencyId;
this.value = value;
}
public String getAssetId() { return assetId; }
public String getCurrencyId() { return currencyId; }
public BigDecimal getValue() { return value; }
@Override
public String toString() {
return "Position{" +
"assetId='" + assetId + '\'' +
", currencyId='" + currencyId + '\'' +
", value=" + value +
'}';
}
}Collectors.toMap方法有多个重载形式,其中一个非常适用于我们当前场景的是:
public static
在一些初次尝试中,开发者可能会先创建一个空的HashMap,然后将其作为mapFactory传递给Collectors.toMap,如下所示:
public class PositionAggregator {
// 模拟获取持仓数据的方法
private List<Position> getPositions(Long portfolioId) {
List<Position> positions = new ArrayList<>();
positions.add(new Position("AAPL", "USD", new BigDecimal("100.50")));
positions.add(new Position("GOOG", "USD", new BigDecimal("200.00")));
positions.add(new Position("AAPL", "USD", new BigDecimal("50.25"))); // 相同键,需要累加
positions.add(new Position("MSFT", "EUR", new BigDecimal("120.75")));
positions.add(new Position("GOOG", "USD", new BigDecimal("75.00"))); // 相同键,需要累加
return positions;
}
public Map<PositionKey, BigDecimal> getAggregatedMap(final Long portfolioId) {
final Map<PositionKey, BigDecimal> map = new HashMap<>(); // 预先创建Map
return getPositions(portfolioId).stream()
.collect(
Collectors.toMap(
position -> new PositionKey(position.getAssetId(), position.getCurrencyId()), // keyMapper
Position::getValue, // valueMapper
(oldValue, newValue) -> oldValue.add(newValue), // mergeFunction
() -> map // mapFactory,引用外部已创建的Map
));
}
}虽然上述代码能够实现功能,但() -> map这种形式将一个外部已创建的Map实例传递给Collectors.toMap作为工厂,这在语义上略显不当。mapFactory的目的是提供一个 新的 空Map实例,供收集器内部使用。直接引用外部Map虽然在此特定情况下可能不会导致错误(因为toMap会清空它或直接使用它),但最佳实践是让收集器完全负责Map的创建。
为了遵循Stream API的函数式编程范式并提高代码的清晰度,我们应该提供一个真正能够创建新HashMap实例的Supplier。最简洁和推荐的方式是使用方法引用HashMap::new。
public class PositionAggregator {
// 模拟获取持仓数据的方法
private List<Position> getPositions(Long portfolioId) {
List<Position> positions = new ArrayList<>();
positions.add(new Position("AAPL", "USD", new BigDecimal("100.50")));
positions.add(new Position("GOOG", "USD", new BigDecimal("200.00")));
positions.add(new Position("AAPL", "USD", new BigDecimal("50.25"))); // 相同键,需要累加
positions.add(new Position("MSFT", "EUR", new BigDecimal("120.75")));
positions.add(new Position("GOOG", "USD", new BigDecimal("75.00"))); // 相同键,需要累加
return positions;
}
/**
* 使用J*a Stream和Collectors.toMap高效聚合持仓数据。
* 当PositionKey冲突时,对BigDecimal值进行累加。
*
* @param portfolioId 投资组合ID
* @return 聚合后的Map<PositionKey, BigDecimal>
*/
public Map<PositionKey, BigDecimal> getAggregatedPositionsMap(final Long portfolioId) {
return getPositions(portfolioId).stream()
.collect(
Collectors.toMap(
position -> new PositionKey(position.getAssetId(), position.getCurrencyId()), // 键映射函数
Position::getValue, // 值映射函数
(oldValue, newValue) -> oldValue.add(newValue), // 合并函数:BigDecimal累加
HashMap::new // Map工厂:提供新的HashMap实例
));
}
public static void main(String[] args) {
PositionAggregator aggregator = new PositionAggregator();
Map<PositionKey, BigDecimal> aggregatedMap = aggregator.getAggregatedPositionsMap(123L);
System.out.println("聚合后的持仓Map:");
aggregatedMap.forEach((key, value) -> System.out.println(key + " -> " + value));
// 预期输出示例:
// PositionKey{assetId='AAPL', currencyId='USD'} -> 150.75
// PositionKey{assetId='GOOG', currencyId='USD'} -> 275.00
// PositionKey{assetId='MSFT', currencyId='EUR'} -> 120.75
}
}在上述代码中,mergeFunction被定义为 (oldValue, newValue) -> oldValue.add(newValue)。这个Lambda表达式的含义是:
对于BigDecimal类型,我们必须使用其add()方法进行数值相加,而不是使用+运算符(因为BigDecimal是对象,+运算符不适用于它)。原始问题中的oldValue != null ? oldValue.add(newValue) : newValue是一个更健壮的写法,它考虑了oldValue可能为null的情况。然而,在大多数实际应用中,如果valueMapper(Position::getValue)始终返回非null的BigDecimal,并且Map中初始值也是非null的,那么oldValue就不会是null,此时直接使用oldValue.add(newValue)是安全且简洁的。如果你的数据源确实可能产生null的BigDecimal值,那么加上null检查会更安全。
通过以上方法,我们可以利用J*a Stream API强大而灵活的Collectors.toMap,以一种优雅且高效的方式,将数据流转换为Map,并根据业务需求对冲突键的值进行聚合。这种模式在处理各种数据转换和汇总任务时都非常有用。
以上就是J*a Stream Collectors:高效聚合Map中现有键的值并求和的详细内容,更多请关注其它相关文章!
# 提供一个
# 无锡本地建设网站
# 定制网站建设服务公司
# 网站SEO优化工程师招聘
# 柳州附近网站建设营销
# 网站推广图
# 温州seo流量
# 泰州产品网站推广哪家好
# 教育行业的营销推广方式
# 品牌全网营销推广平台
# SEO入门单反推荐
# 是一个
# 的是
# 大而
# 配置文件
# java
# 重写
# 自定义
# 运算符
# 转换为
# 持仓
# gate
# 封装性
# java开发
# 金融
# stream
# ai
# app
# go
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
VS Code源代码管理(SCM)视图的进阶使用技巧
cad怎么隐藏指定的图层_cad隐藏或冻结图层方法
《360浏览器》设置摄像头权限方法
《小宇宙》标记不友善评论方法
Sublime怎么格式化HTML代码_Sublime前端代码美化插件使用指南
J*aScript与CSS动画:实现平滑顺序淡入淡出效果并解决显示冲突
《东方财富》条件单关闭方法
动漫岛汉化官网网 动漫岛官方动漫汉化地址
优化2xN网格最大路径和的动态规划算法实践
Flexbox布局中Stencil组件宽度不显示问题解析与:host尺寸控制
PyEZ 配置提交中 RpcTimeoutError 的健壮性处理策略
胃动力不足?试试这5个调理方法
J*aScript文本高亮功能优化:解决多词匹配错误与精确分割策略
抖音号升级成企业资质怎么弄?有什么好处?
VBA Outlook邮件自动化:高效集成Excel数据与列标题的策略
怎样设置开机后自动运行某个程序_Windows启动文件夹与任务计划【自动化】
优化长HTML属性值:SonarQube警告与实用策略
excel怎么计算平均值 excel平均函数*ERAGE使用教学
花生壳内网映射新方案
植物大战僵尸95版游戏版下载_植物大战僵尸95版游戏版安装指南
《花瓣》创建专辑方法
高效调试PHP大型嵌套数组:JSON序列化与可视化工具实践
sublime如何配置PHP开发环境_在sublime中运行与调试PHP代码
小红书网页版首页入口 小红书网页版电脑端官方登录链接
暴风影音官网正式版_暴风影音手机版官网下载安卓
漫蛙官网(首页入口)_漫蛙漫画稳定访问教程分享
如何自定义苹果手机铃声
感染了幽门螺杆菌一定会导致胃癌吗?蚂蚁庄园今日答案最新11.30
iQOO手机信号差网络不稳定怎么办 信号问题原因排查与增强设置【攻略】
rabbitmq 持久化有什么缺点?
抄漫画官网防走失地址_抄漫画最新漫画完整版阅读入口
创建您的便携版VS Code:让配置随身携带
韩剧圈正版官网入口_韩剧圈官方指定登录
微信步数怎么刷_微信步数快速提升技巧
抖音火山版注销账号抖音会注销吗 抖音火山版与抖音账号注销关系
如何使用CSS Grid实现“大方块左侧,小方块右侧垂直堆叠”的水平布局
J*aScript大数运算_BigInt使用指南
《爱笔思画x》魔棒工具抠图教程
126手机126邮箱登录_126邮箱手机登录入口官网
QQ网页版入口导航 QQ网页版在线访问通道
C++ cast类型转换总结_C++ reinterpret_cast与const_cast的使用
解决CSS布局中意外顶部空白问题的教程
在Django中动态检查模型关联:一种灵活的解决方案
《百果园》充值余额方法
mysql中外键约束如何使用_mysql FOREIGN KEY操作
《sketchbook》选中部分图案移动方法
使用Python和NLTK从文本中高效提取名词的实用教程
解决J*aScript动态图片上传中ID重复问题:在同一页面显示多张独立图片
b站如何管理订阅_b站订阅标签分类管理
微信朋友圈怎么设置三天可见 微信朋友圈设置指定天数可见步骤【教程】
2025-12-02
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。