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

文章目录

  • [324. Java Stream API - 实现 `Collector` 接口:自定义你的流式收集器](#324. Java Stream API - 实现 Collector 接口:自定义你的流式收集器)
    • [🔍 为什么要实现 `Collector` 接口?](#🔍 为什么要实现 Collector 接口?)
    • [☕ 自定义 Collector 的三种方式](#☕ 自定义 Collector 的三种方式)
    • [📐 理解 `Collector<T, A, R>` 的三个类型参数](#📐 理解 Collector<T, A, R> 的三个类型参数)
      • [🚗 举例说明](#🚗 举例说明)
    • [📋 代码示例:查看 `Collector` 类型参数](#📋 代码示例:查看 Collector 类型参数)
    • [🧪 补充示例:自定义 Collector 实现(首字母统计)](#🧪 补充示例:自定义 Collector 实现(首字母统计))
    • [🧠 总结:自定义 Collector 的 5 个核心方法](#🧠 总结:自定义 Collector 的 5 个核心方法)
    • [✅ Collector 特性(`characteristics()`)常见值](#✅ Collector 特性(characteristics())常见值)

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 A 与 R 类型相同,finisher() 可直接返回
相关推荐
2501_915918411 分钟前
Windows 上开发 React Native iOS,真机调试、Fast Refresh 与打包 ipa
windows·vscode·react native·ios·个人开发·swift·敏捷流程
FelixZhang02811 分钟前
不懂金融或不会编程,如何开始量化?从策略搭建到回测验证
人工智能·python·算法·数学建模·均值算法·自然语言处理·金融
careathers19 分钟前
【数据结构】基于比较的排序算法 - 插入排序
java·数据结构·排序算法
打工仔折腾 AI28 分钟前
后端部署避坑:吃透IPv4地址、网络接口与CIDR前缀的实际应用
arm开发·人工智能·后端·python·性能优化
街头便衣33 分钟前
OpenRouter周榜复算:164万亿token,输出2.55%,DeepSeek账单占1.4%
android·java·开发语言·人工智能·语言模型·开源
mister_guo37 分钟前
Spring Boot 配置文件读取的几种方式:用法、原理与常见误区
java·spring boot
余槐i38 分钟前
虚拟线程上线后吞吐反而掉了:Spring Boot 3 里 synchronized 锁住载体线程的定位与修复
java·spring boot·性能优化·虚拟线程·jdk 21
要开心吖ZSH41 分钟前
DDD(领域驱动设计)篇三:实战对照——同一个订单业务,两种架构怎么写
java·架构·领域模型·ddd·领域驱动设计·设计思想
wangchunyu11411 小时前
JAVA CompletableFuture异步编程实战
java·开发语言·python
paopaokaka_luck12 小时前
流浪动物救助与收养系统(DeepSeek AI对话、 ECharts多维分析、救助流程衔接、领养与回访衔接、 捐赠确认与公示、志愿者协作)
java·前端·javascript·人工智能·mysql·echarts·推荐算法