网站首页 > java教程 正文
创建自定义收集器是我们在《Java Coding Problem, First Edition》第 9 章,问题 193 中详细涵盖的主题。更具体地说,在那个问题中,你看到了如何通过实现 java.util.stream.Collector 接口来编写自定义收集器。在这个问题中,我们将继续这个旅程,我们将创建几个自定义收集器。这次,我们将依赖于两个具有以下签名的 Collector#of() 方法:
static <T,R> Collector<T,R,R> of(
Supplier<R> supplier,
BiConsumer<R,T> accumulator,
BinaryOperator<R> combiner,
Collector.Characteristics... characteristics)
static <T,A,R> Collector<T,A,R> of(
Supplier<A> supplier,
BiConsumer<A,T> accumulator,
BinaryOperator<A> combiner,
Function<A,R> finisher,
Collector.Characteristics... characteristics)
在这里,T、A 和 R 代表以下内容(从《Java Coding Problems, First Edition》中挑选):
T 代表 Stream 中的元素类型(将被收集的元素) A 代表在收集过程中使用的类型的对象,称为累加器,用于在可变的结果容器中累积流元素。 R 代表收集过程后的对象类型(最终结果) 此外,收集器由四个函数和一个枚举定义。再次,这里是从《Java Coding Problems, First Edition》中的简短挑选:这些函数共同工作,将条目累积到一个可变的结果容器中,并可选地对结果执行最终转换。它们如下:
创建一个新的空的可变结果容器(供应商参数) 将一个新的数据元素并入可变结果容器(累加器参数) 将两个可变结果容器合并为一个(组合器参数) 对可变结果容器执行可选的最终转换以获得最终结果(整理器参数) 此外,我们有 Collector.Characteristics... 枚举,它定义了收集器的行为。可能的值是 UNORDERED(无序)、CONCURRENT(更多线程累积元素)和 IDENTITY_FINISH(整理器是恒等函数,因此不会发生进一步的转换)。
在这种情况下,让我们尝试启动一些示例。但是,首先,让我们假设我们有以下模型:
public interface Vehicle {}
public class Car implements Vehicle {
private final String brand;
private final String fuel;
private final int horsepower;
...
}
public class Submersible implements Vehicle {
private final String type;
private final double maxdepth;
...
}
以及一些数据:
Map<Integer, Car> cars = Map.of(
1, new Car("Dacia", "diesel", 100),
...
10, new Car("Lexus", "diesel", 300)
);
接下来,让我们在名为 MyCollectors 的助手类中有一些收集器。
编写一个收集到 TreeSet 的自定义收集器 在收集到 TreeSet 的自定义收集器中,我们有供应商是 TreeSet::new,累加器是 TreeSet#add(),组合器依赖于 TreeSet#addAll(),整理器是恒等函数:
public static <T>
Collector<T, TreeSet<T>, TreeSet<T>> toTreeSet() {
return Collector.of(TreeSet::new, TreeSet::add,
(left, right) -> {
left.addAll(right);
return left;
}, Collector.Characteristics.IDENTITY_FINISH);
}
在以下示例中,我们使用此收集器将所有电动品牌收集到 TreeSet<String> 中:
TreeSet<String> electricBrands = cars.values().stream()
.filter(c -> "electric".equals(c.getFuel()))
.map(c -> c.getBrand())
.collect(MyCollectors.toTreeSet());
那很容易!
编写一个收集到 LinkedHashSet 的自定义收集器 在收集到 LinkedHashSet 的自定义收集器中,我们有供应商是 LinkedHashSet::new,累加器是 HashSet::add,组合器依赖于 HashSet#addAll(),整理器是恒等函数:
public static <T> Collector<T, LinkedHashSet<T>, LinkedHashSet<T>> toLinkedHashSet() {
return Collector.of(LinkedHashSet::new, HashSet::add,
(left, right) -> {
left.addAll(right);
return left;
}, Collector.Characteristics.IDENTITY_FINISH);
}
在以下示例中,我们使用此收集器收集排序后的汽车马力:
LinkedHashSet<Integer> hpSorted = cars.values().stream()
.map(c -> c.getHorsepower())
.sorted()
.collect(MyCollectors.toLinkedHashSet());
完成!LinkedHashSet<Integer> 包含按升序排列的马力值。
编写一个排除另一个收集器中元素的自定义收集器 本节的目标是提供一个自定义收集器,它接受一个 Predicate 和一个 Collector 作为参数。它将给定的谓词应用于要收集的元素,以便从给定的收集器中排除失败项。
public static <T, A, R> Collector<T, A, R> exclude(
Predicate<T> predicate, Collector<T, A, R> collector) {
return Collector.of(
collector.supplier(),
(l, r) -> {
if (predicate.negate().test(r)) {
collector.accumulator().accept(l, r);
}
},
collector.combiner(),
collector.finisher(),
collector.characteristics()
.toArray(Collector.Characteristics[]::new)
);
}
自定义收集器使用给定收集器的供应商、组合器、整理器和特性。它只影响给定收集器的累加器。基本上,它只对通过给定谓词的元素显式调用给定收集器的累加器。例如,如果我们想通过这个自定义收集器获得小于 200 的排序马力,那么我们可以这样调用它(谓词告诉我们要排除什么):
LinkedHashSet<Integer> excludeHp200 = cars.values().stream()
.map(c -> c.getHorsepower())
.sorted()
.collect(MyCollectors.exclude(c -> c > 200,
MyCollectors.toLinkedHashSet()));
在这里,我们使用了两个自定义收集器,但我们也可以很容易地将 toLinkedHashSet() 替换为内置收集器。挑战自己编写这个自定义收集器的对应部分。编写一个收集通过给定谓词的元素的收集器。
编写一个按类型收集元素的自定义收集器 假设我们有以下 List<Vechicle>:
Vehicle mazda = new Car("Mazda", "diesel", 155);
Vehicle ferrari = new Car("Ferrari", "gasoline", 500);
Vehicle hov = new Submersible("HOV", 3000);
Vehicle rov = new Submersible("ROV", 7000);
List<Vehicle> vehicles = List.of(mazda, hov, ferrari, rov);
我们的目标是只收集汽车或只收集潜水器,而不是两者都收集。为此,我们可以编写一个按类型收集到给定供应商的自定义收集器,如下所示:
public static
<T, A extends T, R extends Collection<A>> Collector<T, ?, R>
toType(Class<A> type, Supplier<R> supplier) {
return Collector.of(supplier,
(R r, T t) -> {
if (type.isInstance(t)) {
r.add(type.cast(t));
}
},
(R left, R right) -> {
left.addAll(right);
return left;
},
Collector.Characteristics.IDENTITY_FINISH
);
}
现在,我们可以将 List<Vechicle> 中的汽车收集到 ArrayList 中,如下所示:
List<Car> onlyCars = vehicles.stream()
.collect(MyCollectors.toType(
Car.class, ArrayList::new));
并且,我们可以将潜水器收集到 HashSet 中,如下所示:
Set<Submersible> onlySubmersible = vehicles.stream()
.collect(MyCollectors.toType(
Submersible.class, HashSet::new));
最后,让我们为自定义数据结构编写一个自定义收集器。
为 Splay Tree 编写自定义收集器 在第 5 章,问题 120 中,我们实现了一个 Splay Tree 数据结构。现在,让我们编写一个能够将元素收集到 Splay Tree 中的自定义收集器。显然,供应商是 SplayTree::new。此外,累加器是 SplayTree#insert(),而组合器是 SplayTree#insertAll():
public static
Collector<Integer, SplayTree, SplayTree> toSplayTree() {
return Collector.of(SplayTree::new, SplayTree::insert,
(left, right) -> {
left.insertAll(right);
return left;
},
Collector.Characteristics.IDENTITY_FINISH);
}
以下是将汽车马力收集到 SplayTree 的示例:
SplayTree st = cars.values().stream()
.map(c -> c.getHorsepower())
.collect(MyCollectors.toSplayTree());
完成!挑战自己实现一个自定义收集器。
- 上一篇: 一维数组的使用详细教程 一维数组什么意思
- 下一篇: 「清晰易懂」数据结构与算法之数组
猜你喜欢
- 2024-12-18 「清晰易懂」数据结构与算法之数组
- 2024-12-18 一维数组的使用详细教程 一维数组什么意思
- 2024-12-18 JAVA集合系列分享-ArrayList java arraylist join
- 2024-12-18 数组在java与C++中的不同 请比较java和c的数组有何不同之处?
- 2024-12-18 深入浅出 JavaScript 数组(建议收藏)
- 2024-12-18 Groovy学习-数组 groovy 数据类型
你 发表评论:
欢迎- 04-27微服务部署架构设计详解(图文全面总结)
- 04-27Java微服务架构选型与对比:一场技术流派的巅峰对决
- 04-27微服务架构下Java的最佳实践
- 04-27Java微服务架构选型:优雅拆分与高效整合
- 04-27微服务架构下的Java代码拆分策略:像拼图一样构建系统
- 04-27微服务架构下的Java最佳实践
- 04-27微服务架构下Java的挑战与机遇
- 04-27微服务架构下Java事务管理的艺术
- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)