Java Stream分组操作全解析:从属性分组到固定大小分块

在Java开发中,我们经常需要对集合进行分组操作。本文将深入讲解两种典型的分组场景:基于元素属性的分组和固定大小的分块,帮助您掌握Collectors.groupingBy()IntStream分块的正确使用方式。

一、基于元素属性的分组:groupingBy()

1. 基本用法

Collectors.groupingBy()是Stream API中用于根据元素属性进行分类的强大工具:

List<Student> students = // 初始化学生列表

// 按照学生所在班级分组
Map<String, List<Student>> studentsByClass = students.stream()
    .collect(Collectors.groupingBy(Student::getClassName));

2. 高级用法

2.1 多级分组
// 先按班级分组,再按性别分组
Map<String, Map<Gender, List<Student>>> multiLevelGroup = students.stream()
    .collect(Collectors.groupingBy(
        Student::getClassName,
        Collectors.groupingBy(Student::getGender)
    ));
2.2 分组后统计
// 统计每个班级的学生人数
Map<String, Long> classCount = students.stream()
    .collect(Collectors.groupingBy(
        Student::getClassName,
        Collectors.counting()
    ));

3. 使用场景

  • 数据分类汇总(如按地区统计销售额)
  • 多维度数据分析
  • 数据预处理和转换

二、固定大小分块:IntStream方案

1. 基本实现

当需要将列表按固定大小分块时,可以使用IntStream方案:

List<Map<String, Object>> list = new ArrayList<>();
// 假设list已经被填充了数据

int groupSize = 50;
List<List<Map<String, Object>>> groupedList = IntStream.range(0, (list.size() + groupSize - 1) / groupSize)
    .mapToObj(i -> list.subList(i * groupSize, Math.min(list.size(), (i + 1) * groupSize)))
    .collect(Collectors.toList());

2. 实现原理

  1. IntStream.range()生成分组索引序列
  2. 计算总组数:(list.size() + groupSize - 1) / groupSize
  3. 使用subList获取每个子列表
  4. 通过Math.min处理最后一组可能不足的情况

3. 使用场景

  • 批量数据处理(如数据库批量插入)
  • 分页处理
  • 大数据量分批处理

三、两种分组方式的对比

特性groupingBy固定大小分块
分组依据元素属性元素位置索引
组大小不固定固定大小
结果类型Map<K, List>List<List>
典型应用场景数据分类统计批量处理/分页

四、实际应用案例

案例1:电商订单处理

// 按订单状态分组
Map<OrderStatus, List<Order>> ordersByStatus = orders.stream()
    .collect(Collectors.groupingBy(Order::getStatus));

// 将订单按每100个一批处理
List<List<Order>> batches = IntStream.range(0, (orders.size() + 99) / 100)
    .mapToObj(i -> orders.subList(i * 100, Math.min(orders.size(), (i + 1) * 100)))
    .collect(Collectors.toList());

案例2:日志分析

// 按日志级别分组统计
Map<LogLevel, Long> logCountByLevel = logs.stream()
    .collect(Collectors.groupingBy(
        Log::getLevel,
        Collectors.counting()
    ));

// 将日志按每500条一批写入文件
List<List<Log>> logBatches = IntStream.range(0, (logs.size() + 499) / 500)
    .mapToObj(i -> logs.subList(i * 500, Math.min(logs.size(), (i + 1) * 500)))
    .collect(Collectors.toList());

五、性能注意事项

  1. groupingBy在数据量大时可能产生性能问题,考虑使用并行流:

    Map<K, List<T>> result = list.parallelStream()
        .collect(Collectors.groupingByConcurrent(...));
    
  2. 固定大小分块时,subList创建的是视图而非新集合,修改会影响原列表

  3. 对于超大集合,考虑使用迭代器方式逐步处理而非一次性加载

六、总结

  • 使用groupingBy进行基于属性的逻辑分组
  • 使用IntStream+subList进行固定大小的物理分块
  • 根据业务场景选择合适的分组策略
  • 注意不同分组方式对性能和内存的影响

掌握这两种分组技术,您将能够更高效地处理Java集合数据,满足各种业务场景的需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hi星尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值