使用Java Stream按文件大小总和阈值分组自定义FileInfo对象列表
问题描述
现有自定义类FileInfo:
public class FileInfo { private String filePath; private Long fileSize; private String status; // 需提供getFileSize()等getter方法 }
以及List<FileInfo>类型的文件列表,需要将其拆分为多个子列表,每个子列表中所有元素的fileSize总和不超过指定阈值,最终返回List<List<FileInfo>>类型结果。已通过普通for-each循环实现,现需改用Java Stream/Collectors完成。
解决方案思路与实现
Java Stream原生没有直接支持这种基于累加阈值的拆分操作,因为Stream的多数操作是无状态的,而该需求需要维护累加状态(当前子列表的大小总和)。我们可以通过自定义Collector实现,核心逻辑如下:
- 定义一个状态容器类,跟踪当前子列表的元素、累计大小,以及最终的拆分结果;
- 实现Collector的核心方法:提供容器、累加元素(判断是否需要新建子列表)、合并多线程容器(适配并行流)、转换为最终结果。
具体代码实现
1. 定义状态容器类
class BatchHolder { private final long threshold; private List<FileInfo> currentBatch = new ArrayList<>(); private long currentSize = 0L; private List<List<FileInfo>> result = new ArrayList<>(); public BatchHolder(long threshold) { this.threshold = threshold; } // 向当前批次添加元素,超过阈值则归档当前批次并新建 public void add(FileInfo file) { long fileSize = file.getFileSize(); if (currentSize + fileSize > threshold && !currentBatch.isEmpty()) { result.add(currentBatch); currentBatch = new ArrayList<>(); currentSize = 0L; } currentBatch.add(file); currentSize += fileSize; } // 处理最后一个未归档的批次 public List<List<FileInfo>> finish() { if (!currentBatch.isEmpty()) { result.add(currentBatch); } return result; } // 合并两个BatchHolder,适配并行流场景 public BatchHolder merge(BatchHolder other) { this.result.addAll(other.result); // 将另一个容器的当前批次元素逐个加入,保证阈值规则 other.currentBatch.forEach(this::add); return this; } }
2. 实现自定义Collector
public static Collector<FileInfo, BatchHolder, List<List<FileInfo>>> batchBySize(long threshold) { return Collector.of( // 提供状态容器实例 () -> new BatchHolder(threshold), // 累加元素到容器 BatchHolder::add, // 合并多线程的容器结果 BatchHolder::merge, // 转换为最终的拆分列表 BatchHolder::finish ); }
3. 使用示例
List<FileInfo> fileList = ...; // 你的原始文件列表 long sizeThreshold = 1024 * 1024; // 示例阈值:1MB List<List<FileInfo>> batchedList = fileList.stream() .collect(batchBySize(sizeThreshold));
关键注意点
- 必须保证
FileInfo类提供getFileSize()的getter方法,否则无法获取文件大小; - 若单个文件的
fileSize超过阈值,该文件会单独成为一个子列表; - 并行流场景下,
combiner方法会正确合并不同线程的批次,确保最终结果符合阈值规则; - 该实现通过Collector封装状态,避免了外部状态修改的线程安全问题,符合Stream规范。
内容的提问来源于stack exchange,提问作者Neha
相关产品推荐
相关产品推荐

