用map替代内层遍历可将查找从o(n)降为o(1),时间复杂度由o(n²)降至o(n);关键在于以共用业务主键(如userid)为key构建单向映射,注意key唯一性、非空校验及类型一致,并分两步执行:先建map后批量查取。

直接用 Map 替代内层遍历,把“找数据”从 O(N) 降成 O(1),时间复杂度就从平方级变成线性级。核心不是删循环,而是换查找方式。
找准关联字段,构建单向映射
两个集合能匹配,一定有共用的业务主键(如 billId、userId、classId)。这个字段就是 Map 的 key,值通常是整个对象或关键属性。
- 确保 key 唯一:若存在重复 key,
Collectors.toMap默认会抛异常,可用合并函数兜底,比如(v1, v2) -> v1保留第一个 - 避免空指针:提前过滤掉 null key,例如
.filter(d -> d.getBillId() != null) - 类型一致:key 类型必须严格匹配,
Long和String即使值相同也无法命中
先建索引,再批量查取
不要边循环边建 Map,而要分两步走:第一步一次性把被查找集合转成 Map;第二步遍历主集合,用 key 直接 get。
- 建 Map 示例:
Map<long account> accountMap = accountList.stream().collect(Collectors.toMap(Account::getUserId, Function.identity()))</long> - 查取示例:
Account account = accountMap.get(user.getUserId()),判空后即可使用 - 不推荐在循环里反复调用
list.stream().filter(...).findFirst(),那还是 O(N) 查找
注意边界与内存权衡
Map 优化不是银弹,得看场景是否适配。
- 数据量小(
- 内存紧张环境(如 Android 低配机型、嵌入式设备),Map 占用堆空间明显,需评估 GC 压力
- 需要一对多匹配(一个 key 对应多个对象)时,改用
groupingBy构建Map<k list>></k>
验证效果,别只信直觉
优化前后用真实数据跑一次耗时对比,尤其关注数据量翻倍时的性能变化曲线。
- 5 万 × 3 万 的双重循环,实测常超 26 秒;同样数据用 Map 通常压到 300ms 内
- 用
System.nanoTime()精确计时,避开 JVM 预热和 GC 干扰 - 观察 GC 日志,确认 Map 创建没引发频繁 Young GC











