《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)
扩展性 ⭐⭐⭐ 任意修改逻辑 ⭐☆☆ 只能分两路 ⭐⭐⭐ 可随时加分组 ⭐☆☆ 加组需改遍历
相关推荐
deviant-ART1 小时前
为什么 Java 编译器要求 catch 块显式 return 或 throw
java·开发语言
知兀1 小时前
【IDEA/基本设置】主题、字体、导包;Code Style配置(google的Java Code Stytle);git提交优化import
java·ide·intellij-idea
A_nanda1 小时前
C#类型转换最佳实践
java·jvm·c#
华科易迅1 小时前
Spring AOP(XML后置+异常通知)
xml·java·spring
jgbazsh1 小时前
Spring中把一个bean对象交给Spring容器管理的三种方式
java·后端·spring
Network porter1 小时前
IDEA2025(2025.2)都更新了什么???
java·intellij-idea·idea
twc8291 小时前
不可言说的知识:AI时代软件工程的核心传递问题
java·人工智能·大模型·软件工程·知识工程
林九生1 小时前
【Claude Code】Claude Code 接入阿里云百炼 Coding Plan 完整配置教程(Linux版)
linux·阿里云·云计算
华仔啊2 小时前
前端不懂 Java?后端怕 CSS?这套AI全栈方案专治各种偏科
java·前端·后端