Java™ 教程(聚合操做)

聚合操做

你使用集合作什么?你不可能简单地将对象存储在集合中并将它们留在那里,在大多数状况下,使用集合检索存储在其中的项。java

再次考虑Lambda表达式小节中描述的场景,假设你正在建立一个社交网络应用程序,你但愿建立一个功能,使管理员可以对知足某些条件的社交网络应用程序的成员执行任何类型的操做,例如发送消息。git

如前所述,假设这个社交网络应用程序的成员由如下Person类表示:github

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中包含的全部成员的名称:segmentfault

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

下面的示例打印集合roster中包含的全部成员,但使用集合操做forEach数组

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

尽管在本例中,使用聚合操做的版本比使用for-each循环的版本要长,可是你将看到,对于更复杂的任务,使用批量数据操做的版本将更加简洁。网络

在示例BulkDataOperationsExamples中找到本节中描述的代码摘录。数据结构

管道和流

管道是聚合操做的序列,下面的示例打印集合roster中包含的男性成员,其中包含由聚合操做filterforEach组成的管道:函数

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

将此示例与下面的示例进行比较,下面的示例打印集合roster中包含的男性成员,并使用for-each循环:code

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

管道包含如下组件:对象

  • 源:能够是集合、数组、生成器函数或I/O通道,在本例中,源是集合roster
  • 零或多个中间操做,中间操做(如filter)生成一个新的流。

    流是元素的序列,与集合不一样,它不是存储元素的数据结构,相反,流经过管道携带来自源的值,这个示例经过调用方法stream从集合roster建立一个流。

    filter操做返回一个新的流,其中包含与其predicate匹配的元素(此操做的参数),在本例中,predicate是lambda表达式e -> e.getGender() == Person.Sex.MALE。若是对象egender字段的值为Person.Sex.MALE,则返回布尔值true,所以,本例中的filter操做返回一个包含集合roster中全部男性成员的流。
  • 一个终端操做,终端操做(如forEach)生成非流结果,如原始值(如双精度值)、集合,或者在forEach的状况下,根本没有值。在本例中,forEach操做的参数是lambda表达式e -> System.out.println(e.getName()),调用对象e上的getName方法(Java运行时和编译器推断对象e的类型是Person)。

下面的示例计算集合roster中包含的全部男性成员的平均年龄,其中管道由聚合操做filtermapToIntaverage组成:

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包含许多终端操做,好比average,经过组合流的内容返回一个值,这些操做称为概括操做。

聚合操做和迭代器之间的区别

forEach这样的聚合操做看起来像迭代器,然而,它们有几个根本的区别:

  • 它们使用内部迭代:聚合操做不包含像next这样的方法来指示它们处理集合的下一个元素,使用内部委托,应用程序决定迭代什么集合,可是JDK决定如何迭代集合,使用外部迭代,你的应用程序将肯定它迭代哪些集合以及如何迭代。然而,外部迭代只能按顺序迭代集合的元素,内部迭代没有这种限制,它能够更容易地利用并行计算,这涉及到将一个问题划分为子问题,同时解决这些问题,而后将子问题的解的结果组合起来。
  • 它们处理来自流的元素:聚合操做处理流中的元素,而不是直接从集合中聚合,所以,它们也称为流操做。
  • 它们支持将行为做为参数:能够将lambda表达式指定为大多数聚合操做的参数,这使你可以自定义特定聚合操做的行为。

上一篇:SortedMap接口

相关文章
相关标签/搜索