一个接口要临时聚合几十万条数据,你会直接用 HashMap 吗?容量、冲突和内存怎么评估?
我的判断
几十万条聚合可以用 HashMap,但要先估算去重后的键数、对象大小和请求并发;内存预算不够时应改成分批或下推聚合。
我不会看到“几十万”就直接 new 一个 HashMap。先估算最终有多少个 key,而不是输入多少行;再用压测或 JOL 看 key、value、Node 和对象本身的占用。假设 30 万个聚合项每项连同对象开销约 100B,一个请求就接近 30MB,并发 20 个已经可能把堆顶满。
确认内存可接受后,会按预估键数设置初始容量,避免多次扩容:
int capacity = (int) Math.ceil(expectedKeys / 0.75d);
Map<Long, Summary> result = new HashMap<>(capacity);
key 要有稳定、分布正常的 hashCode。聚合值尽量原地累计,别为每条输入创建新的临时对象。如果 key 是基本类型且场景很热,可以评估 primitive map,减少装箱。
如果预算不够,我会优先让数据库按键聚合、按分区流式处理,或者把请求改成异步任务,而不是单纯加大 -Xmx。最后用真实基数和并发观察堆峰值、分配速率与 GC,而不是只测单次接口耗时。
思路拆解问题分析
这题的关键不是 HashMap 的红黑树阈值,而是 一次请求的内存乘以并发数。容量、冲突和扩容只是一部分,key/value 对象、装箱、输入列表是否同时驻留,往往才是主要占用。