你使用集合做什么?你不可能简单地将对象存储在集合中并将它们留在那里,在大多数情况下,使用集合检索存储在其中的项。
再次考虑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()); }
下面的示例打印集合 roster
中包含的所有成员,但使用集合操作 forEach
:
roster .stream() .forEach(e -> System.out.println(e.getName());
尽管在本例中,使用聚合操作的版本比使用 for-each 循环的版本要长,但是你将看到,对于更复杂的任务,使用批量数据操作的版本将更加简洁。
在示例 BulkDataOperationsExamples 中找到本节中描述的代码摘录。
管道是聚合操作的序列,下面的示例打印集合 roster
中包含的男性成员,其中包含由聚合操作 filter
和 forEach
组成的管道:
roster .stream() .filter(e -> e.getGender() == Person.Sex.MALE) .forEach(e -> System.out.println(e.getName()));
将此示例与下面的示例进行比较,下面的示例打印集合 roster
中包含的男性成员,并使用 for-each
循环:
for (Person p : roster) { if (p.getGender() == Person.Sex.MALE) { System.out.println(p.getName()); } }
管道包含以下组件:
roster
。 filter
)生成一个新的流。 stream
从集合 roster
创建一个流。 filter
操作返回一个新的流,其中包含与其 predicate
匹配的元素(此操作的参数),在本例中, predicate
是lambda表达式 e -> e.getGender() == Person.Sex.MALE
。如果对象 e
的 gender
字段的值为 Person.Sex.MALE
,则返回布尔值 true
,因此,本例中的 filter
操作返回一个包含集合 roster
中所有男性成员的流。 forEach
)生成非流结果,如原始值(如双精度值)、集合,或者在 forEach
的情况下,根本没有值。在本例中, forEach
操作的参数是lambda表达式 e -> System.out.println(e.getName())
,调用对象 e
上的 getName
方法(Java运行时和编译器推断对象 e
的类型是 Person
)。
下面的示例计算集合 roster
中包含的所有男性成员的平均年龄,其中管道由聚合操作 filter
、 mapToInt
和 average
组成:
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决定如何迭代集合,使用外部迭代,你的应用程序将确定它迭代哪些集合以及如何迭代。然而,外部迭代只能按顺序迭代集合的元素,内部迭代没有这种限制,它可以更容易地利用并行计算,这涉及到将一个问题划分为子问题,同时解决这些问题,然后将子问题的解的结果组合起来。 上一篇:SortedMap接口