Java 流处理之收集器详解
Java 8 引入了一个新的 Stream API,其中的收集器(Collector)是 Java 8 可以处理流(Stream)中数据的一个关键工具。收集器是指将流中元素转换成不同形式的操作。在本文中,我们将详细介绍 Java 中的收集器。
收集器的基本概念
Java 8 提供了 22 个预定义的收集器。这些收集器和终止操作结合使用来对流进行收集。Java 中的收集器具有以下基本特征:
- 可转换为 List、Set 或 Map 等集合数据结构;
- 可以限制结果集的长度;
- 可以通过分组、分区等方式对结果进行分组;
- 可以自定义将流中的元素聚合成一个集合的方式。
收集器分类
Java 中的收集器主要分为以下几类:
1. 转换收集器
转换收集器可以将流中的元素转换为指定类型的集合。Java 提供的转换收集器有以下几种:
- toList():将流转换为 List 集合;
- toSet():将流转换为 Set 集合;
- toMap():将流转换为 Map,Key 和 Value 由 map 接收的两个函数推断出来。
以下是 toList() 和 toSet() 的示例代码:
List<String> list = Arrays.asList("Java", "Python", "C++");
List<String> result = list.stream().collect(Collectors.toList());
System.out.println(result); // [Java, Python, C++]
Set<String> set = Arrays.asList("Java", "Python", "C++", "Java").stream().collect(Collectors.toSet());
System.out.println(set); // [Java, Python, C++]
2. 分组收集器
分组收集器可以将流中的元素按条件分组。Java 提供的分组收集器有以下几种:
- groupingBy():按照分组条件进行分组;
- partitioningBy():按照分区条件进行分区。
以下是 groupingBy() 的示例代码:
class Product {
private String name;
private String category;
// 省略 getters 和 setters 方法
}
List<Product> productList = Arrays.asList(
new Product("A", "Category1"), new Product("B", "Category2"),
new Product("C", "Category1"), new Product("D", "Category2"));
Map<String, List<Product>> result = productList.stream().collect(Collectors.groupingBy(Product::getCategory));
System.out.println(result);
// 输出:
// {Category1=[Product{name='A', category='Category1'},
// Product{name='C', category='Category1'}],
// Category2=[Product{name='B', category='Category2'},
// Product{name='D', category='Category2'}]}
3. 聚合收集器
将流中的元素聚合成一个值。Java 提供的聚合收集器有以下几种:
- reducing():对流中的元素进行聚合,最终返回一个 Optional;
- summarizingInt()、summarizingDouble()、summarizingLong():将流中的元素转换为 Int、Double 或 Long 类型,然后进行汇总操作。
以下是 reducing() 和 summarizingInt() 的示例代码:
// 使用 reducing() 计算元素的总和
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream().collect(Collectors.reducing(Integer::sum));
System.out.println(sum); // Optional[15]
// 使用 summarizingInt() 计算元素的平均值、最大值、最小值、总和等
List<Integer> numbers2 = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = numbers2.stream().collect(Collectors.summarizingInt(Integer::intValue));
System.out.println(stats.getAverage()); // 3.0
System.out.println(stats.getMax()); // 5
System.out.println(stats.getMin()); // 1
System.out.println(stats.getSum()); // 15
收集器的使用
1. 如何定义自定义收集器
我们可以通过实现 Collector 接口来定义自己的收集器。Collector 接口定义了四个方法:
- supplier():提供一个空的累加器(即初始容器),用于累加收集的元素;
- accumulator():将元素 T 添加到累加器 A 中,并且在添加完后,累加器必须按原有顺序无副作用的处理前一个累加器,然后返回一个新的累加器;
- combiner():将 A1 和 A2 两个累加器合并成一个新的累加器,通常与accumulator() 方法相反,其中 A2 代表 accumulator() 方法返回的新容器,A1 代表旧容器;
- finisher():对最终容器进行转换,例如将累加器转换成期望的返回类型,或对其进行最终的变换。
下面是示例代码:
class Person {
private String name;
private int age;
// 省略 getters 和 setters 方法
}
class PersonStatistics {
int count;
int totalAge;
double averageAge;
public PersonStatistics() {
this.count = 0;
this.totalAge = 0;
this.averageAge = 0.0;
}
public void accept(Person person) {
count++;
totalAge += person.getAge();
averageAge = (double) totalAge / (double) count;
}
public PersonStatistics combine(PersonStatistics other) {
count += other.count;
totalAge += other.totalAge;
averageAge = (double) totalAge / (double) count;
return this;
}
public void finish() {
// do nothing
}
public double getAverageAge() {
return this.averageAge;
}
// 省略 toString() 方法
}
class PersonCollector implements Collector<Person, PersonStatistics, PersonStatistics> {
@Override
public Supplier<PersonStatistics> supplier() {
return PersonStatistics::new;
}
@Override
public BiConsumer<PersonStatistics, Person> accumulator() {
return PersonStatistics::accept;
}
@Override
public BinaryOperator<PersonStatistics> combiner() {
return PersonStatistics::combine;
}
@Override
public Function<PersonStatistics, PersonStatistics> finisher() {
return personStatistics -> {
personStatistics.finish();
return personStatistics;
};
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
}
List<Person> personList = Arrays.asList(
new Person("Hank", 33),
new Person("Charlie", 31),
new Person("Emily", 26),
new Person("Michael", 58)
);
PersonStatistics personStatistics = personList.stream().collect(new PersonCollector());
System.out.println(personStatistics);
输出结果:
PersonStatistics{count=4, totalAge=148, averageAge=37.0}
2. 如何使用自定义收集器
要使用自定义收集器,需要将其传递给 Stream 的 collect() 方法。以下是示例代码:
List<Person> personList = Arrays.asList(
new Person("Hank", 33),
new Person("Charlie", 31),
new Person("Emily", 26),
new Person("Michael", 58)
);
PersonStatistics personStatistics = personList.stream().collect(new PersonCollector());
System.out.println(personStatistics);
输出结果:
PersonStatistics{count=4, totalAge=148, averageAge=37.0}
小结
Java 中的收集器是处理流中数据的重要工具。本文主要介绍了收集器的基本概念、分类、使用方法和自定义方法。在实际的开发过程中,需要根据具体业务需求来选择不同的收集器。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 流处理之收集器详解 - Python技术站