文档

Java™ 教程
隐藏目录
聚合操作
导航: 集合

课程:聚合操作

注意:为了更好地理解本节中的概念,请查阅Lambda表达式方法引用部分。

你用集合做什么?你不仅仅是把对象存储在集合中然后不管它们了。在大多数情况下,你使用集合来检索存储在其中的项。

再次考虑Lambda表达式部分中描述的情景。假设你正在创建一个社交网络应用程序。你想要创建一个功能,使管理员能够对满足特定条件的社交网络应用程序成员执行任何类型的操作,例如发送消息。

和之前一样,假设这个社交网络应用程序的成员由以下Person类表示:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;
    
    // ...

    public int getAge() {
        // ...
    }

    public String getName() {
        // ...
    }
}

以下示例使用for-each循环打印出集合roster中所有成员的姓名:

for (Person p : roster) {
    System.out.println(p.getName());
}

以下示例使用聚合操作forEach打印出集合roster中的所有成员:

roster
    .stream()
    .forEach(e -> System.out.println(e.getName());

虽然在这个例子中,使用聚合操作的版本比使用for-each循环的版本更长,但你会发现,对于更复杂的任务,使用批量数据操作的版本会更简洁。

本节涵盖以下主题:

在示例BulkDataOperationsExamples中找到本节中描述的代码片段。

流水线和流

一个流水线是一系列的聚合操作。以下示例使用由聚合操作filterforEach组成的流水线来打印出集合roster中的男性成员:

roster
    .stream()
    .filter(e -> e.getGender() == Person.Sex.MALE)
    .forEach(e -> System.out.println(e.getName()));

将这个示例与使用for-each循环打印包含在集合roster中的男性成员的示例进行比较:

for (Person p : roster) {
    if (p.getGender() == Person.Sex.MALE) {
        System.out.println(p.getName());
    }
}

一个流水线包含以下组件:

下面的示例使用由聚合操作filtermapToIntaverage组成的流水线,计算了集合roster中所有男性成员的平均年龄:

double average = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge)
    .average()
    .getAsDouble();

mapToInt操作返回一个新的IntStream类型的流(它是一个只包含整数值的流)。该操作将其参数中指定的函数应用于特定流中的每个元素。在这个示例中,函数是Person::getAge,它是一个返回成员年龄的方法引用。(或者,你可以使用lambda表达式e -> e.getAge()。)因此,在这个示例中,mapToInt操作返回一个包含集合roster中所有男性成员年龄的流。

average操作计算IntStream类型流中包含的元素的平均值。它返回一个OptionalDouble类型的对象。如果流不包含元素,则average操作返回一个空的OptionalDouble实例,并且调用getAsDouble方法会抛出一个NoSuchElementException异常。JDK包含许多返回通过组合流的内容而得到一个值的终端操作,这些操作被称为规约操作;更多信息请参见规约部分。

聚合操作与迭代器之间的区别

聚合操作(如forEach)看起来像迭代器,但它们有几个根本的区别:


上一页: 上一课程
下一页: 简化