Java核心技术·卷Ⅱ:高级特性(原书第10版)
上QQ阅读APP看书,第一时间看更新

1.1 从迭代到流的操作

在处理集合时,我们通常会迭代遍历它的元素,并在每个元素上执行某项操作。例如,假设我们想要对某本书中的所有长单词进行计数。首先,将所有单词放到一个列表中:

流的版本比循环版本要更易于阅读,因为我们不必扫描整个代码去查找过滤和计数操作,方法名就可以直接告诉我们其代码意欲何为。而且,循环需要非常详细地指定操作的顺序,而流却能够以其想要的任何方式来调度这些操作,只要结果是正确的即可。

仅将stream修改为parallelStream就可以让流库以并行方式来执行过滤和计数。

流遵循了“做什么而非怎么做”的原则。在流的示例中,我们描述了需要做什么:获取长单词,并对它们计数。我们没有指定该操作应该以什么顺序或者在哪个线程中执行。相比之下,本节开头处的循环要确切地指定计算应该如何工作,因此也就丧失了进行优化的机会。

流表面上看起来和集合很类似,都可以让我们转换和获取数据。但是,它们之间存在着显著的差异:

1.流并不存储其元素。这些元素可能存储在底层的集合中,或者是按需生成的。

2.流的操作不会修改其数据源。例如,filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。

3.流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。例如,如果我们只想查找前5个长单词而不是所有长单词,那么filter方法就会在匹配到第5个单词后停止过滤。因此,我们甚至可以操作无限流。

让我们再来看看这个示例。stream和parallelStream方法会产生一个用于words列表的stream。filter方法会返回另一个流,其中只包含长度大于12的单词。count方法会将这个流化简为一个结果。

这个工作流是操作流时的典型流程。我们建立了一个包含三个阶段的操作管道:

1.创建一个流。

2.指定将初始流转换为其他流的中间操作,可能包含多个步骤。

3.应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。从此之后,这个流就再也不能用了。

在程序清单1-1中的示例中,流是用stream或parallelStream方法创建的。filter方法对其进行转换,而count方法是终止操作。

程序清单1-1 streams/CountLongWords.java

在下一节中,你将会看到如何创建流。后续的三个小节将处理流的转换。再后面的五个小节将讨论终止操作。

java.util.stream.Stream<T>8

·Stream<T> filter(Predicate<? super T> p)

产生一个流,其中包含当前流中满足P的所有元素。

·long count()

产生当前流中元素的数量。这是一个终止操作。

java.util.Collection<E>1.2

·default Stream<E> stream()

·default Stream<E> parallelStream()

产生当前集合中所有元素的顺序流或并行流。