326. Java Stream API - 实现自定义的 toList() 与 toSet() 收集器
在 Java 的 Stream API 中,Collectors.toList() 和 Collectors.toSet() 是最常见的两个内置收集器。但你是否好奇它们背后的原理?今天我们就带大家手动实现一个行为等同于 toList() 的收集器,并了解如何基于它改造为 toSet() 收集器。
📦 实现一个自定义 toList() 收集器
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 收集器
Collection<String> strings = List.of("one", "two", "three", "four", "five");
List<String> result = strings.stream()
.collect(new ToList<>()); // 使用我们自定义的收集器
System.out.println("result = " + result);
💡 输出结果:
result = [one, two, three, four, five]
🔄 将其改造成 toSet() 收集器
我们只需要修改两处,就能实现一个等价于 Collectors.toSet() 的收集器:
✅ 修改 1:使用 HashSet 作为容器
public Supplier<Set<T>> supplier() {
return HashSet::new;
}
✅ 修改 2:声明该收集器是无序的
public Set<Characteristics> characteristics() {
return Set.of(
Characteristics.IDENTITY_FINISH,
Characteristics.UNORDERED // 不保证处理顺序
);
}
🧪 ToSet 收集器完整实现示例
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特性(配合线程安全结构)。