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。
  • ✅ 想避免不必要的转换? 当 A 和 R 类型一致时,记得声明 IDENTITY_FINISH。
  • ✅ 并行流支持? 如果想支持并行执行,还可以考虑加入 CONCURRENT 特性(配合线程安全结构)。
相关推荐
ServBay几秒前
本地模型已经跑起来了,Copilot Fusion 为什么还要联网?
后端·ai编程·github copilot
WebInfra25 分钟前
Rspack 2.3 发布:联手 SWC 团队带来全新编译器
前端·javascript·github
武子康39 分钟前
H3-World 的动作没按时发生,先检查时间线变成了什么
人工智能·后端·agent
苍何41 分钟前
腾讯 Buddy One 来了,办公全家桶一键打通WorkBuddy(附教程)
后端
GIS数据转换器1 小时前
基于CIM的城市智慧供热监管服务平台
java·大数据·开发语言·人工智能·后端·物联网·struts
小卿噢1 小时前
同样是插 10 万行,最快的写法比最慢的快 103 倍
前端
weixin_417396831 小时前
星级酒店境外电视频道怎么合法接入?合规要点与核对清单
开发语言·前端·php·1024程序员节·综合布线
adfreedom2 小时前
如何避免每个业务系统中都有几十个枚举查询
后端
hiahiahia1232 小时前
Structured Output
前端·人工智能
m0_639906942 小时前
SpringBoot自动配置原理
java·spring boot·后端