324. Java Stream API - 实现 Collector 接口:自定义你的流式收集器

324. Java Stream API - 实现 Collector 接口:自定义你的流式收集器

在 Java Stream API 中,Collector 是终端操作 collect() 背后的核心接口。如果你希望完全掌控数据收集的过程------比如构造特定的数据结构、在并行流中定制合并方式等------那就需要了解如何实现这个接口。


🔍 为什么要实现 Collector 接口?

虽然大多数时候我们可以使用 Java 提供的标准收集器(如 toList()groupingBy()joining()),但当你需要:

  • 创建一个自定义结构(如:合并值到一个特殊格式的字符串、构造非标准 Map 等)
  • 控制并行流合并逻辑(比如收集到线程安全容器)
  • 优化性能(跳过不必要的转换)

那么实现 Collector 接口就是最灵活且强大的方式。


☕ 自定义 Collector 的三种方式

方法 描述 适用场景
✅ 使用 Collectors 工厂方法 组合已有收集器,或配合 collectingAndThen() 做轻度自定义 推荐优先使用
✅ 使用 Stream.collect(supplier, accumulator, combiner) 自行传入构造、累加与合并逻辑 中等灵活性
✅❗ 实现 Collector<T, A, R> 接口 完全掌控所有阶段,适合高度定制 最灵活,但技术门槛高

📐 理解 Collector<T, A, R> 的三个类型参数

我们先看一下接口定义:

java 复制代码
interface Collector<T, A, R> {
    ...
}
  • T:流中元素的类型
  • A:中间可变容器类型(Accumulator 类型)
  • R:最终返回结果类型(Result 类型)

🚗 举例说明

收集器 T(流中元素) A(中间容器) R(最终结果)
toList() String ArrayList<String> List<String>
toSet() String HashSet<String> Set<String>
groupingBy(String::length, counting()) String 内部 Map + Counter Map<Integer, Long>

➡️ 注意:我们平时用 Collectors.toList() 等时,看不到 A 的具体类型,是因为返回类型中对它进行了隐藏(用 ? 代替了)。


📋 代码示例:查看 Collector 类型参数

java 复制代码
Collection<String> strings = List.of(
    "two", "three", "four", "five", "six", "seven", "eight", "nine",
    "ten", "eleven", "twelve"
);

// toList
Collector<String, ?, List<String>> listCollector = Collectors.toList();
List<String> list = strings.stream().collect(listCollector);
System.out.println("Size of list = " + list.size()); // 11

// toSet
Collector<String, ?, Set<String>> setCollector = Collectors.toSet();
Set<String> set = strings.stream().collect(setCollector);
System.out.println("Size of set = " + set.size()); // 11

// groupingBy + counting
Collector<String, ?, Map<Integer, Long>> groupingCollector =
    Collectors.groupingBy(String::length, Collectors.counting());
Map<Integer, Long> map = strings.stream().collect(groupingCollector);
System.out.println("Size of map = " + map.size()); // 4

这些例子中,? 代表我们无需关心中间类型 A------由 API 实现者决定,我们只关心结果类型 R。


🧪 补充示例:自定义 Collector 实现(首字母统计)

我们实现一个 collector,用于按单词首字母统计出现次数:

java 复制代码
class FirstLetterCountCollector implements Collector<String, Map<Character, Integer>, Map<Character, Integer>> {

    @Override
    public Supplier<Map<Character, Integer>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<Character, Integer>, String> accumulator() {
        return (map, word) -> {
            char first = word.charAt(0);
            map.merge(first, 1, Integer::sum);
        };
    }

    @Override
    public BinaryOperator<Map<Character, Integer>> combiner() {
        return (map1, map2) -> {
            map2.forEach((k, v) -> map1.merge(k, v, Integer::sum));
            return map1;
        };
    }

    @Override
    public Function<Map<Character, Integer>, Map<Character, Integer>> finisher() {
        return Function.identity(); // 直接返回中间容器
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of(Characteristics.IDENTITY_FINISH);
    }
}

使用:

java 复制代码
List<String> words = List.of("apple", "apricot", "banana", "blueberry", "cherry");

Map<Character, Integer> counts = words.stream()
    .collect(new FirstLetterCountCollector());

System.out.println(counts); // {a=2, b=2, c=1}

🧠 总结:自定义 Collector 的 5 个核心方法

方法 作用
supplier() 创建中间结果容器
accumulator() 累加流中的每个元素
combiner() 并行流中合并两个中间结果
finisher() 将中间容器转换为最终结果
characteristics() 提供 collector 的特性,如是否为并发、安全等

✅ Collector 特性(characteristics())常见值

特性 说明
CONCURRENT 支持并发(accumulator 线程安全)
UNORDERED 不依赖顺序(如 Set 类型)
IDENTITY_FINISH AR 类型相同,finisher() 可直接返回
相关推荐
三水不滴2 小时前
SpringBoot + Redis 滑动窗口计数:打造高可靠接口防刷体系
spring boot·redis·后端
若水不如远方2 小时前
分布式一致性原理(四):工程化共识 —— Raft 算法
分布式·后端·算法
老迟聊架构2 小时前
深入理解低延迟与高吞吐:从架构哲学到技术抉择
后端·架构
yma162 小时前
前端react模拟内存溢出——chrome devtool查找未释放内存
前端·chrome·react.js
hrhcode2 小时前
【Netty】一.Netty架构设计与Reactor线程模型深度解析
java·spring boot·后端·spring·netty
三水不滴2 小时前
千万级数据批处理实战:SpringBoot + 分片 + 分布式并行处理方案
spring boot·分布式·后端
colicode2 小时前
Objective-C语音验证码接口API示例代码:老版iOS应用接入语音验证教程
前端·c++·ios·前端框架·objective-c
会算数的⑨2 小时前
Spring AI Alibaba 学习(三):Graph Workflow 深度解析(下篇)
java·人工智能·分布式·后端·学习·spring·saa
小圣贤君2 小时前
从「脑内人设」到「一眼入魂」:51mazi 小说人物图 AI 生成实战
前端·人工智能·文生图·ai写作·通义万相·写作软件·小说人物