Java自定义Collector:能否通过累加类型直接生成对应Collector?
Collector包含三个泛型类型:
public interface Collector<T, A, R>
其中A是归约操作的可变累加类型(通常作为实现细节隐藏)。
要创建自定义Collector,我需要编写两个类:
- 自定义累加类型类
- 自定义Collector本身类
有没有库函数或技巧,可以接收累加类型并直接返回对应的Collector?
简单示例
这个示例只是用来说明问题,我知道这种场景用reduce就能实现,但这不是我想要的方案。我还有一个更复杂的示例,因为篇幅太长就不放在这里了,但核心思路和这个一致。
假设我要收集流的总和,最后返回String类型。
我可以先实现累加器类:
public static class SumCollector { Integer value; public SumCollector(Integer value) { this.value = value; } public static SumCollector supply() { return new SumCollector(0); } public void accumulate(Integer next) { value += next; } public SumCollector combine(SumCollector other) { return new SumCollector(value + other.value); } public String finish(){ return Integer.toString(value); } }
然后通过这个类创建Collector:
Collector.of(SumCollector::supply, SumCollector::accumulate, SumCollector::combine, SumCollector::finish);
但所有方法都要引用另一个类,感觉很奇怪,我觉得应该有更直接的实现方式。
我也考虑过让这个类实现Collector<Integer, SumCollector, String>,但这样会导致代码重复(比如supplier()方法要返回SumCollector::supply之类的)。
简化方案:让累加器类实现Collector接口并复用自身方法
其实可以让你的累加器类直接实现Collector接口,通过复用自身方法避免代码重复。修改后的SumCollector示例如下:
public static class SumCollector implements Collector<Integer, SumCollector, String> { Integer value; public SumCollector(Integer value) { this.value = value; } @Override public Supplier<SumCollector> supplier() { return SumCollector::supply; } @Override public BiConsumer<SumCollector, Integer> accumulator() { return SumCollector::accumulate; } @Override public BinaryOperator<SumCollector> combiner() { return SumCollector::combine; } @Override public Function<SumCollector, String> finisher() { return SumCollector::finish; } @Override public Set<Characteristics> characteristics() { // 根据需求设置特性,比如并行场景可添加CONCURRENT、UNORDERED等 return Collections.emptySet(); } // 保留原有核心方法 public static SumCollector supply() { return new SumCollector(0); } public void accumulate(Integer next) { value += next; } public SumCollector combine(SumCollector other) { return new SumCollector(value + other.value); } public String finish(){ return Integer.toString(value); } }
这样你直接用SumCollector的实例即可作为Collector使用,无需再调用Collector.of。
轻量方案:匿名内部类直接定义逻辑
如果是逻辑简单的场景,也可以跳过单独的累加器类,直接在Collector.of中用匿名内部类或方法引用定义所有逻辑:
Collector<Integer, AtomicInteger, String> sumCollector = Collector.of( AtomicInteger::new, AtomicInteger::addAndGet, (a1, a2) -> { a1.addAndGet(a2.get()); return a1; }, ai -> Integer.toString(ai.get()) );
这种方式无需额外创建类,适合快速实现简单Collector。
第三方库辅助(可选)
Guava等第三方库提供了一些Collector封装工具,比如Collectors2中的方法,但本质还是基于JDK的Collector.of扩展。如果不想引入依赖,JDK原生的Collector.of已经是最直接的工具。
内容的提问来源于stack exchange,提问作者Ricola

