《Java 数据分组的四种姿势:从 for 循环到 Stream API》

分离SideType=0.5和1的数据

for循环

java 复制代码
        // 3. 分离SideType=0.5和1的数据
        List<SlaughterWeigh> needMergeList = new ArrayList<>();
        List<SlaughterWeigh> normalList = new ArrayList<>();

        for (SlaughterWeigh record : originalList) {
            if ("0.5".equals(record.getSideType())) {
                needMergeList.add(record);
            } else {
                normalList.add(record);
            }
        }

Stream 流方案

方案 1:使用 Collectors.partitioningBy(推荐)

java 复制代码
Map<Boolean, List<SlaughterWeigh>> partitioned = originalList.stream()
    .collect(Collectors.partitioningBy(r -> "0.5".equals(r.getSideType())));

List<SlaughterWeigh> needMergeList = partitioned.get(true);   // 边口类型 0.5
List<SlaughterWeigh> normalList = partitioned.get(false);       // 其他

特点:

  • 专为二分类设计(true/false)
  • 底层优化,使用 Partition 对象,内存效率高
  • 不会返回 null,空分组返回空列表 []
  • 只能分两组,不能扩展

输出结构:

Map<Boolean, List<SlaughterWeigh>>

├── true → 记录1(0.5), 记录3(0.5), ... // needMergeList

└── false → 记录2(1), 记录4(null), ... // normalList

方案 2:使用 Collectors.groupingBy

java 复制代码
Map<String, List<SlaughterWeigh>> grouped = originalList.stream()
    .collect(Collectors.groupingBy(r -> {
        if ("0.5".equals(r.getSideType())) return "half";
        else if ("1".equals(r.getSideType())) return "whole";
        else return "unknown";
    }));

List<SlaughterWeigh> halfList = grouped.getOrDefault("half", new ArrayList<>());
List<SlaughterWeigh> wholeList = grouped.getOrDefault("whole", new ArrayList<>());
List<SlaughterWeigh> unknownList = grouped.getOrDefault("unknown", new ArrayList<>());

特点:

  • 可分多组(2 组、3 组、N 组)
  • 键可以是任意类型(String、Enum 等)
  • 可能返回 null(需用 getOrDefault)
  • 适合复杂分类场景

你的场景:只分两组(0.5 和非 0.5),用 groupingBy 有点大材小用

方案 3:两次 filter(效率较低,不推荐)

java 复制代码
List<SlaughterWeigh> needMergeList = originalList.stream()
    .filter(r -> "0.5".equals(r.getSideType()))
    .collect(Collectors.toList());

List<SlaughterWeigh> normalList = originalList.stream()
    .filter(r -> !"0.5".equals(r.getSideType()))
    .collect(Collectors.toList());

特点:

  • 代码看着简洁
  • 性能最差:遍历两次列表
  • 列表很大时(如 10 万条),耗时是其他方式的 2 倍
  • 每增加一个分组就要多遍历一次

对比

方案 优点 缺点
当前 for 循环 直观易懂,一次遍历 代码稍长
partitioningBy 一次遍历,返回 Map 需要理解 API
groupingBy 灵活,可多分组 稍复杂
两次 filter 简洁 遍历两次,效率低
对比维度 for 循环 partitioningBy groupingBy 两次 filter
代码行数 6 行 4 行 4 行 4 行
遍历次数 1 次 ✅ 1 次 ✅ 1 次 ✅ 2 次 ❌
时间复杂度 O(n) O(n) O(n) O(2n)
创建列表数 2 个 2 个(在 Map 中) 2 个(在 Map 中) 2 个
空值安全性 完全控制 不会返回 null,空列表自动处理 可能返回 null(需 getOrDefault) 不会返回 null
适用分组数 任意 只能 2 组(true/false) 任意多组 每组需一次遍历
你的场景适配 ⭐⭐⭐ 完美适配 ⭐⭐⭐ 完美适配(二分类) ⭐⭐☆ 杀鸡用牛刀 ⭐☆☆ 性能差
可读性(新手) ⭐⭐⭐ 最直观 ⭐⭐☆ 需了解 API ⭐⭐☆ 需了解 API ⭐⭐⭐ 直观
可读性(熟练) ⭐⭐☆ 略啰嗦 ⭐⭐⭐ 函数式简洁 ⭐⭐⭐ 函数式简洁 ⭐☆☆ 重复遍历
调试难度 ⭐⭐⭐ 易调试(可打断点) ⭐⭐☆ 需熟悉 Stream ⭐⭐☆ 需熟悉 Stream ⭐⭐⭐ 易调试
空列表处理 已创建,可直接用 返回空列表(非 null) 可能返回 null 返回空列表(非 null)
扩展性 ⭐⭐⭐ 任意修改逻辑 ⭐☆☆ 只能分两路 ⭐⭐⭐ 可随时加分组 ⭐☆☆ 加组需改遍历
相关推荐
文丑颜不良啊35 分钟前
LeetCode2481~2500题目解析
java·leetcode
修己xj1 小时前
nslookup 使用指南:两种模式与常用查询技巧
linux
Flynn Liang2 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪2 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息3 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼3 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
唠玖馆4 小时前
文件系统详解
linux·文件系统·ext2
GeW4 小时前
当Linux内核开始“确定性”,制造高端化才有了底层语法
linux
SL_staff4 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈
SL_staff5 小时前
企业AI落地难?JVS-AI渐进式实践:安全可控、小切口验证、标准化沉淀与规模化复制
java·开源·aigc