boxed() 使原始类型流能使用通用 Collector,因原始流 collect() 不支持标准 Collector 接口;自定义 Collector 才实现分块归集,如每 3 个元素一组的分块需先 boxed() 再传入含 supplier/accumulator/combiner/finisher 的 Collector。

Java Stream API 中,boxed() 本身只是将原始类型流(如 IntStream)转为装箱后的引用类型流(如 Stream<integer></integer>),它不直接参与分块归集;真正实现“专属分块归集”的核心是自定义 Collector。而 boxed() 的作用在于让原始流能接入通用的、面向对象的 Collector(因为原始流不支持泛型 Collector)。所以关键不是“boxed 配合 Collector”,而是:先用 boxed() 拓宽类型能力,再用自定义 Collector 实现按需分块聚合。
为什么需要 boxed() 才能接 custom Collector?
原始类型流(IntStream、LongStream、DoubleStream)的 collect() 方法只接受专为其设计的三参数形式(supplier, accumulator, combiner),不支持标准的 Collector<t a r></t> 接口。而业务中常用的分块逻辑(比如每 N 个元素一组、按条件切片、累积到阈值后截断)往往需要状态维护和灵活终止判断——这必须依赖通用 Collector 的四阶段模型(supplier/accumulator/combiner/finisher)。
因此,若源头是 IntStream.range(1, 100),想按每 5 个数分组为 List<list>></list>,就得先调用 .boxed() 转成 Stream<integer></integer>,才能传入你写的 Collector<integer list>>></integer>。
写一个按固定大小分块的 custom Collector
以“每 3 个元素一组”为例,目标是把 Stream<integer></integer> 归集为 List<list>></list>:
立即学习“Java免费学习笔记(深入)”;
-
Supplier:提供一个可变容器,比如
ArrayList<arraylist>></arraylist> - Accumulator:每次来一个元素,放入当前子列表;若当前子列表满(size==3),就新建一个子列表
- Combiner:合并两个分块结果时,把右侧所有子列表 addAll 到左侧(注意:需保证右侧每个子列表已完整)
- Finisher:可选,此处无需额外处理,直接返回结果
代码示意:
Collector<Integer, ArrayList<ArrayList<Integer>>, List<List<Integer>>> chunkingCollector = Collector.of(
() -> new ArrayList<>(),
(lists, item) -> {
if (lists.isEmpty() || lists.get(lists.size() - 1).size() == 3) {
lists.add(new ArrayList<>());
}
lists.get(lists.size() - 1).add(item);
},
(left, right) -> {
// 合并时,把 right 的每个 chunk 追加到 left
left.addAll(right);
return left;
},
ArrayList::new // finisher,这里用构造器引用即可
);更实用的场景:带状态感知的动态分块
比如按连续递增序列分块——[1,2,3,5,6,8] → [[1,2,3], [5,6], [8]]。这时 accumulator 需记住上一个值:
- Supplier 返回一个包含
currentChunk和lastValue的 holder 对象(如 record 或自定义类) - Accumulator 判断是否连续:若
item == lastValue + 1,追加;否则新建 chunk 并更新 lastValue - Combiner 要谨慎:两个 holder 合并时,需确保右侧第一个元素能接上左侧最后一个元素(否则会错断),通常更适合用
sequential()或避免并行流
这种逻辑无法靠 Collectors.groupingBy() 或 partitioningBy() 实现,必须靠自定义 Collector 的 accumulator 状态管理能力。
使用时注意并行安全与性能
自定义 Collector 默认可能被用于并行流,但分块操作天然有顺序依赖。除非你的分块规则完全无状态(比如纯按索引取模),否则:
- 避免在 accumulator 中修改共享可变状态(如 static 变量)
- combiner 必须满足结合律,且不能破坏分块语义(例如上面的“连续递增”场景,combiner 很难安全合并,建议显式调用
stream.sequential()) - 如果只是简单固定大小分块,可考虑用第三方库如 Guava 的
Lists.partition()或 Apache Commons Collections 的CollectionUtils.partition(),它们内部已优化,比手写 Collector 更稳妥


















