简介
一个流表示一个元素的序列,并支持不同类型的操作,以达到预期的结果。流的源头通常是一个集合或一个数组,数据从那里流出来。
流在几个方面与集合不同;最明显的是,流不是一个存储元素的数据结构。它们在本质上是功能性的,值得注意的是,对一个流的操作会产生一个结果,通常会返回另一个流,但不会修改其来源。
为了 "巩固 "这些变化,你将一个流的元素收集回一个Collection 。
寻找算术平均数的数学操作是我们相当频繁使用的一种操作,我们有许多方法来执行它。
在本指南中,我们正是要这样做--我们将看看如何通过Collectors 类中的内置方法来获得Java中不同数字类型的平均值/平均值。
**注意:**值得注意的是,如果元素是数值型的,你可以对其本身进行平均,如果不是数值型的,你也可以将其还原为数值型的表示,然后对还原部分进行平均。
收集器和Stream.collect()
收集器代表了Collector 接口的实现,它实现了各种有用的还原操作,例如将元素累积到集合中,根据特定的参数总结元素,等等。
所有预定义的实现都可以在
Collectors类中找到。
你也可以非常容易地实现你自己的收集器,并使用它来代替预定义的收集器--你可以用内置的收集器走得很远,因为它们涵盖了你可能想要使用它们的绝大多数情况。
为了能够在我们的代码中使用这个类,我们需要导入它。
import static java.util.stream.Collectors.*;
Stream.collect() 在流的元素上执行一个可变的还原操作。
可变还原操作在处理流中的元素时,将输入元素收集到一个可变容器中,比如Collection 。
*averaging_()*收集器的定义
Collectors 类有各种有用的功能,恰好它包含了一些允许我们找到输入元素平均值的功能。
它们一共有三个。
Collectors.averagingInt(),Collectors.averagingDouble()和Collectors.averagingLong()。
让我们先来看看这些方法的签名。
public static <T> Collector<T,?,Double> averagingInt(ToIntFunction<? super T> mapper)
public static <T> Collector<T,?,Double> averagingDouble(ToDoubleFunction<? super T> mapper)
public static <T> Collector<T,?,Double> averagingLong(ToLongFunction<? super T> mapper)
**注意:**方法签名中的通用T 代表了我们要处理的输入元素的类型。
ToIntFunction,ToDoubleFunction 和ToLongFunction 来自java.util.function ,使我们能够执行从对象类型到其原始int,double 或long 领域的转换(还原)。让我们定义一个Student 类,我们可以将其还原为一个数字字段。
public class Student {
private String name;
private Double grade;
private Integer age;
private Long examsTaken;
// Constructor, getters and setters
让我们也把我们的学生实例化在一个List 。
List<Student> students = Arrays.asList(
new Student("John", 7.56, 21, 17L),
new Student("Jane", 8.31, 19, 9L),
new Student("Michael", 9.17, 20, 14L),
new Student("Danielle", 9.17, 21, 23L),
new Student("Joe", 8.92, 22, 25L)
);
除了自定义对象--我们还将看看我们如何在原始数据类型上使用平均化收集器--也就是Lists ,只由数字元素组成。
Collectors.averagingInt()
该方法返回一个Collector ,产生应用于输入元素的整数值函数的算术平均值。在没有元素存在的情况下,结果是0 。
由于所有的平均数方法都很直接,我们将直接进入一个例子。
两个例子中的第一个将使用一个简单的List ,由Integers 。 假设我们有以下内容。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Double average = numbers.stream().collect(Collectors.averagingInt(Integer::intValue));
System.out.println(average);
我们应用.stream() 方法来创建一个Integer 对象的流,之后我们使用之前讨论过的.collect() 方法,用averagingInt() 采集器来采集这个流。
由于数值已经是整数,通过方法引用获得intValue ,有效地进行了1对1的映射,作为我们的ToIntFunction ,因为不需要转换。
3.0
接下来,在我们的Student 类中,唯一的整数值字段是age 。在下面的例子中,我们将计算所有学生的平均年龄。
Double averageAge = students.stream().collect(Collectors.averagingInt(Student::getAge));
System.out.println("Average student age is: " + averageAge);
当使用来自用户定义类的字段时,其内部工作原理是相同的,唯一的区别是我们不能对Student 实例进行平均,所以我们将其还原为年龄。本例中的ToIntFunction 是对getAge() 方法的引用,通过它,我们将 Student 减少到他们的年龄。
这个过程在我们接下来的两个方法中也是一样的,唯一改变的是我们引用哪个方法的转换函数。我们的代码片断的输出是。
Average student age is: 20.6
Collectors.averagingDouble()
这个方法返回一个Collector ,产生一个应用于输入元素的双值函数的算术平均值。在没有元素存在的情况下,结果是0 。
Collectors.averagingDouble() 与averagingInt() 有一点不同,因为它是用双数工作的。由于累积的四舍五入误差,返回的平均数可能会根据处理数值的顺序而变化。按递增顺序排序的数值往往会产生更准确的结果。
如果任何一个值是NaN ,或者任何一点的总和是NaN ,那么平均值也将是NaN 。此外,双数格式可以用-253到253范围内的所有连续整数来表示。
让我们来计算一个双数列表的平均值。
List<Double> numbers = Arrays.asList(3.0, 8.0, 4.0, 11.0);
Double average = numbers.stream().collect(Collectors.averagingDouble(Double::doubleValue));
System.out.println(average);
这样就得出了
6.5
现在,让我们把这个方法应用于我们的Student 类。grade 字段是一个双数,所以我们将使用对该字段的getter的方法引用作为averagingDouble() 调用中的ToDoubleFunction 。
Double averageGrade = students.stream().collect(Collectors.averagingDouble(Student::getGrade));
System.out.println("Average student grade is: " + averageGrade);
运行这个方法给我们带来了以下输出
Average student grade is: 8.62
Collectors.averagingLong()
最后一个平均化方法是Collectors.averagingLong() 。这个方法和前面两个一样,返回一个Collector ,产生应用于输入元素的长值函数的算术平均值。如果没有元素平均,则返回0 。
与前两个方法一样,我们可以很容易地对一个长值列表进行平均。
List<Long> numbers = Arrays.asList(10L, 15L, 1L, 3L, 7L);
Double average = numbers.stream().collect(Collectors.averagingDouble(Long::longValue));
System.out.println(average);
这样的结果是
7.2
最后,我们的Student 类有一个长值examsTaken 字段,我们可以计算它的平均值。我们将使用一个对getExamsTaken() 方法的引用作为ToLongFunction 。
Double averageExamsTaken = students.stream().
collect(Collectors.averagingLong(Student::getExamsTaken));
System.out.println("Average exam taken per student is: " + averageExamsTaken);
运行这个方法的输出
Average exam taken per student is: 17.6
结论
很多时候,我们需要计算多个值的平均值,使用Collectors 类中提供的*averaging_()*方法是在 Java 中实现这一目的的最简单和最有效的方法之一。
在本指南中,我们浏览了上述类中提供的所有三个方法,解释了它们在一个用户定义类的例子中是如何工作的,并展示了我们如何在代码中使用它们来达到我们的目的。