三、流和并行

2018-01-23  本文已影响0人  小Noodles

引入流

那么,流到底是什么呢?简短的定义就是“从支持数据处理操作的源生成的元素序列”

1.元素序列:就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序 值。
2.源:流会使用一个提供数据的源,如集合、数组或输入/输出资源。
3.数据处理操作:流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中 的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执 行,也可并行执行。
4.流水线:很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。
5.内部迭代:与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。

return dishes.stream()
                .filter(d -> d.getCalories() < 400)
                .sorted(comparing(Dish::getCalories))
                .map(Dish::getName)
                .limit(3)
                .collect(toList());
在本例中,我们先是对menu调用stream方法,由菜单得到一个流。数据源是菜肴列表(菜 单),它给流提供一个元素序列。接下来,对流应用一系列数据处理操作:filter、map、limit 和collect。除了collect之外,所有这些操作都会返回另一个流,这样它们就可以接成一条流 水线,于是就可以看作对源的一个查询。最后,collect操作开始处理流水线,并返回结果. 2-流-示例.png

外部迭代和内部迭代

使用Collection接口需要用户去做迭代(比如用for-each),这称为外部迭代。 相反, Streams库使用内部迭代——它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出 一个函数说要干什么就可以了.

1.中间操作
诸如filter或sorted等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查 询。重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理
2.终端操作
终端操作会从流的流水线生成结果
3.流使用
总而言之,流的使用一般包括三件事:

使用流

让Stream API管理如何处理数据。这样Stream API就可 以在背后进行多种优化。使用内部迭代的话,Stream API可以决定并行运行你的代码。这 要是用外部迭代的话就办不到了,因为你只能用单一线程挨个迭代。

flatMap


2-流-flatmap.png

用流收集数据

前一个例子清楚地展示了函数式编程相对于指令式编程的一个主要优势:你只需指出希望的 结果——“做什么”,而不用操心执行的步骤——“如何做”。在上一个例子里,传递给collect 方法的参数是Collector接口的一个实现,也就是给Stream中元素做汇总的方法,toList只是说“按顺序给每个元素生成一个列表”;在本例中,groupingBy说的是“生成一个 Map,它的键是(货币)桶,值则是桶中那些元素的列表”。


2-流-收集.png 2-流-分组多级.png 2-流-分组嵌套.png

并行数据处理与性能

高效使用并行流

把顺序流转成并行流轻而易举,但却不一定是好事。我们在本节中
已经指出,并行流并不总是比顺序流快。此外,并行流有时候会和你的直觉不一致,所
以在考虑选择顺序流还是并行流时,第一个也是最重要的建议就是用适当的基准来检查
其性能
1.留意装箱。自动装箱和拆箱操作会大大降低性能。 Java 8中有原始类型流(IntStream、
LongStream、 DoubleStream)来避免这种操作,但凡有可能都应该用这些流
2.有些操作本身在并行流上的性能就比顺序流差。特别是limit和findFirst等依赖于元
素顺序的操作,它们在并行流上执行的代价非常大
3.还要考虑流的操作流水线的总计算成本
4.对于较小的数据量,选择并行流几乎从来都不是一个好的决定
5.考虑流背后的数据结构是否易于分解
6.考虑终端操作中合并步骤的代价是大是小

上一篇 下一篇

猜你喜欢

热点阅读