在Stream类,有这么一句话:
A sequence of elements supporting sequential and parallel aggregate operations.
简单翻译过来就是支持顺序和并行汇集的一系列元素,咋一看,跟集合有点类似,对,在Java8的API中,它允许你用函数式的方式对集合进行操作,是集合运算和表达的高阶抽象。
Collection接口,自带有stream默认方法,如某普通的ArrayList对象,可以直接调用该方法构建stream流流:
//new一个普通list对象
List<Integer> list = new ArrayList<Integer>() {{ add(1);add(2);add(3); }};
//调用默认方法stream转化成Stream流
list.stream();
用Stream类的静态方法of构建Stream流
//Stream.of用元素直接构建流
Stream.of (1,2,3);
在基本操作之前,我们得了解两个基本概念:
用构建章节的例子说明:
list.stream().filter(num -> num > 1).count();
stream(): 构建一个Stream流。
filter(num -> num > 1): 中间操作:过滤集合里大于1的数,惰性,未执行。
count(): 最终操作:计算大于1的数的个数,开始遍历执行。
列举下Stream的操作分类:
Stream操作分类 | ||
---|---|---|
中间操作(Intermediate Operations) | 无状态(Stateless) | unordered() filter() map() mapToInt() mapToLong()mapToDouble() flatMap() flatMapToInt() flatMapToLong()flatMapToDouble() peek() |
有状态(Stateful) | distinct() sorted() limit() skip() | |
最终操作(Terminal Operations) | 非短路操作 | forEach() forEachOrdered() toArray() reduce() collect() max() min() count() |
短路操作(short-circuiting) | anyMatch() allMatch() noneMatch() findFirst() findAny() |
解释一下上表的中几个其他概念:
中间操作的无状态:
即某个Stream流调用无状态的中间操作方法时,可以独立的对Stream流里的某一个元素进行操作,此操作不会受到其他元素的影响。如map()操作,对流里的每个元素进行映射操作。
中间操作的有状态:
即某个Stream流调用有状态的中间操作方法时,要拿到Stream流的所有元素才能操作。
如distinct() 操作,得拿到所有元素,才能进行去重操作,而不是拿某一个元素,就开始处理。
最终操作的短路操作:
即找到符合条件的元素后就能得到最终结果。如findFirst(),找到符合条件的第一个,就结束操作。
最终操作的非短路操作:
即处理所有元素后才能得到最终结果。如forEach() ,循环处理所有元素。
过滤操作,操作章节,已经展示,不重复了。
收集操作,用于将Stream流的元素收集起来,转化成一个list,用上面的例子,写法如
list.stream().collect(Collectors.toList())
map操作可以将Stream流里的元素,从一种类型转换成另一种类型,即一种流转换成另一个流。下面例子简单展示了,map()将Stream流里的每个Integer元素转化成String类型。
list.stream()
.map((num)->Integer.toString(num))
.collect(Collectors.toList())
flatMap 扁平化的map转化,为了理解,我们设计一个需求,分割字符串“Hello World”成单个字母,并且去重后,转化成list,输出。
我们很容易会想到用map这样的操作,如下:
Stream.of("Hello World")
.map(str->str.split(""))
.distinct()
.collect(Collectors.toList());
输出(省去了输出打印的代码):
[H, e, l, l, o, , W, o, r, l, d]
但这样不行,Stream.of(“Hello World”)是构建了一个元素的Stream流, 而map操作恰恰是针对单个元素进行处理,所以仅仅是把单个"Hello World"元素转化成单个String[]类型元素, 像distinct操作要需要多个元素的才能处理,所以做如下修改:
Stream.of("Hello World")
.map(str->str.split(""))
.flatMap(Stream::of)
.distinct()
.collect(Collectors.toList());
用flatMap操作,把上面map操作转化的String[]类型元素构建成多个Stream流(每个String[]里的String都构建成一个流),使distinct操作起作用。
输出:
[H, e, l, o, , W, r, d]
reduce 操作可以实现从一组值中生成一个值,比如说实现一个累加操作。
Stream.of(1, 2, 3) .reduce(0, (acc, element) -> acc + element);
将两个参数相加,acc 是累加器,保存着当前的 累加结果。
输出:6
获取Stream并行流,比如说多个任务,利用多个和多核cpu并行执行(原理是fork/join框架),提高运行的效率,但不代表一定比串行快,受数据大小、集合数据结构、值是否装箱、cpu核数、处理每个元素的时间的影响,所以举个比较夸张的例子,让parallel体现出效果。
IntStream.range(0, 1000000000)
.parallel()
.summaryStatistics()
.getSum()
构建0-1000000000的Stream流,parallel操作获取并行Stream后,进行统计求和。这时候执行效率如下:
串行情况(即不使用parallel操作)
499999999500000000
运行时间为:1382ms
并行情况(使用parallel操作)
499999999500000000
运行时间为:393ms
以上的例子,较为简单,主要是考虑如何更快理解,实际使用场景,还需是结合实际,多查阅Java8 Stream Api,做到举一反三。
欢迎大家留言讨论,同时欢迎转载,但请注明出处和作者。
所有示例,已经上传至我的github https://github.com/zhiwen-li/Exemplar-learning