326. Java Stream API - 实现自定义的 toList() 与 toSet() 收集器

326. Java Stream API - 实现自定义的 toList()toSet() 收集器

在 Java 的 Stream API 中,Collectors.toList()Collectors.toSet() 是最常见的两个内置收集器。但你是否好奇它们背后的原理?今天我们就带大家手动实现一个行为等同于 toList() 的收集器 ,并了解如何基于它改造为 toSet() 收集器。


📦 实现一个自定义 toList() 收集器

java 复制代码
class ToList<T> implements Collector<T, List<T>, List<T>> {

    @Override
    public Supplier<List<T>> supplier() {
        return ArrayList::new; // 创建一个空的 ArrayList 作为中间容器
    }

    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return Collection::add; // 将元素累加到 List 中
    }

    @Override
    public BinaryOperator<List<T>> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2); // 合并两个 List(用于并行流)
            return list1;
        };
    }

    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.identity(); // 直接返回中间容器,不需要额外转换
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of(Characteristics.IDENTITY_FINISH); // 说明 finisher 是 identity
    }
}

🚀 使用我们的 ToList 收集器

java 复制代码
Collection<String> strings = List.of("one", "two", "three", "four", "five");

List<String> result = strings.stream()
    .collect(new ToList<>()); // 使用我们自定义的收集器

System.out.println("result = " + result);

💡 输出结果:

java 复制代码
result = [one, two, three, four, five]

🔄 将其改造成 toSet() 收集器

我们只需要修改两处,就能实现一个等价于 Collectors.toSet() 的收集器:

✅ 修改 1:使用 HashSet 作为容器

java 复制代码
public Supplier<Set<T>> supplier() {
    return HashSet::new;
}

✅ 修改 2:声明该收集器是无序的

java 复制代码
public Set<Characteristics> characteristics() {
    return Set.of(
        Characteristics.IDENTITY_FINISH,
        Characteristics.UNORDERED // 不保证处理顺序
    );
}

🧪 ToSet 收集器完整实现示例

java 复制代码
class ToSet<T> implements Collector<T, Set<T>, Set<T>> {

    @Override
    public Supplier<Set<T>> supplier() {
        return HashSet::new;
    }

    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        return Set::add;
    }

    @Override
    public BinaryOperator<Set<T>> combiner() {
        return (set1, set2) -> {
            set1.addAll(set2);
            return set1;
        };
    }

    @Override
    public Function<Set<T>, Set<T>> finisher() {
        return Function.identity();
    }

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

🎯 总结一下关键点

元素 toList() toSet()
容器类型 ArrayList HashSet
是否无序 ❌(有序) ✅(无序)
特性声明 IDENTITY_FINISH IDENTITY_FINISH, UNORDERED

🧠 小贴士

  • 想提升性能? 在能接受无序的场景中使用 UNORDERED
  • 想避免不必要的转换?AR 类型一致时,记得声明 IDENTITY_FINISH
  • 并行流支持? 如果想支持并行执行,还可以考虑加入 CONCURRENT 特性(配合线程安全结构)。
相关推荐
不超限2 分钟前
InfoSuite AS部署Vue项目
前端·javascript·vue.js
程序员小寒3 分钟前
JavaScript设计模式(五):装饰者模式实现与应用
前端·javascript·设计模式
wefly20177 分钟前
零基础上手m3u8live.cn,免费无广告的M3U8在线播放器,电脑手机通用
前端·javascript·学习·电脑·m3u8·m3u8在线播放
晓131312 分钟前
React篇——第四章 React Router基础
前端·javascript·react
晨非辰12 分钟前
Git版本控制速成:提交三板斧/日志透视/远程同步15分钟精通,掌握历史回溯与多人协作安全模型
linux·运维·服务器·c++·人工智能·git·后端
Moment13 分钟前
如果想转 AI 全栈?推荐你学一下 Langchain!
前端·后端·面试
顾北1213 分钟前
Elasticsearch DSL 从入门到实战:全文检索 + 地理查询 + SpringBoot 整合全攻略
后端·elasticsearch
大鸡腿同学14 分钟前
AI编程时代来临
后端
cch891816 分钟前
常见布局实现详解(Flex 实战版)
前端·javascript·css