什么是Stream,为什么需要Stream
Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。
同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的java.util.stream 是一个函数式语言+多核时代综合影响的产物。
----这段介绍引用自IBM的《Java 8 中的 Streams API 详解》
文章写的非常好,给我很大启发,链接会在文末给出
流的使用过程
使用流的过程分为三个步骤:
1.创建一个流
2.对其进行操作(可以是多个操作)
3.关闭一个流
1.创建流
java8提供了多种构造流的方法
Collection
数组
BufferedReader
静态工厂
自己构建
其他
创建流的示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 List<String> strList = new ArrayList <>(); strList.add("HuHanShi" ); strList.add("HuBlanker" ); String[] strings = {"HuHanShi" , "HuBlanker" }; Stream<String> stream = strList.stream(); stream = strList.parallelStream(); stream = Arrays.stream(strings); stream = Stream.of(strings); stream = new BufferedReader (new InputStreamReader (System.in)).lines(); IntStream streamInt = IntStream.range(0 , 10 );streamInt = new Random ().ints(); stream = Pattern.compile("," ).splitAsStream("wo,w,wa,a" ); stream.forEach(System.out::println);
2.操作流
当把数据结构包装成流之后,就要开始对里面的元素进行各种操作了。
流的操作分为两种:
Intermediate:这类型的方法不会修改原来的流,而是会返回一个新的流以便后续操作。例如:map,filter,sorted.
Terminal:这类型的方法会真正的将流进行遍历,在使用过后,流也将会被“消耗”,无法继续操作。
接下来将对常用的(我看过的)流的操作方法一一举例说明:
map()
对当前的流进行一个操作并将得到的结果包装成一个新的流返回。
1 2 strList.stream().map(String::toUpperCase).collect(Collectors.toList());
flatMap()
flatMap与map的区别是他会将stream内部的结构扁平化,对每一个值都将其转化为一个流,最后将所有刘扁平化为一个流返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 Stream<List<Integer>> moreStream = Stream .of(Arrays.asList(1 , 8 ), Arrays.asList(2 , 4 ), Arrays.asList(2 , 4 , 5 , 6 )); moreStream.flatMap(Collection::stream).forEach(System.out::println); ``` 输出结果为:1 ,8 ,2 ,4 ,2 ,4 ,5 ,6. 而不是:[1 ,8 ],[2 ,4 ],[2 ,4 ,5 ,6 ]. **filter()** filter 对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream。 ```java strList.stream().filter(perStr -> perStr.contains("Hu" )).forEach(System.out::print); ``` **forEach()** forEach 方法接收一个 Lambda 表达式,然后在 Stream 的每一个元素上执行该表达式。 forEach是Terminal操作,当遍历完成时,流被消耗无法继续对其进行操作。 错误示例: ```java strList.stream().forEach(System.out::print).map(); ``` **peek()** 有人要问了,我想对每一个元素进行操作一下但是后续还要用怎么办呢,当然是有办法的,那就是peek()方法。 ```java strList.stream().peek(System.out::print).map(perStr->perStr.toUpperCase()).collect(Collectors.toList()); ``` **reduce()** 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。 ```java Integer sum = moreStream.flatMap(Collection::stream).reduce(0 ,(a,b)->a+b); ``` 这个例子将<a herf="#flatmap" >flatMap</a>中的结果进行了累加操作。 reduce()还可以用与字符串连接,求最大最小值等等。 **sorted()** 对stream中的值进行排序。 ```java strList.sort(String::compareTo); ``` 相比于数组的排序,stream的排序可以先剔除掉一些不需要排序的值,可以减少无用操作。 **接下来将一些原理(类型)差不多的放一起说一哈。** **limit()/skip()** 取前n个元素/跳过前n个元素。 ```java strList.stream().limit(2 ).skip(1 ).forEach(System.out::print); ``` **findFirst()** 取stream得第一个值,值得一提的是返回值为Optional<>. Optional是一个容器,可以包含一个值,使用它可以尽量避免NullPointException。 具体见:<a herf="http://your123.com/2018/03/11/java8-Optional%E7%B1%BB%E5%88%9D%E4%BD%93%E9%AA%8C/" >java8 Optional 类初体验</a> ```java String first = strList.stream().findFirst().get(); ``` **min()/max()/distinct()** 取最小/最大/无重复值。 min和max操作可以通过reduce方法实现,但是因为经常使用所以单独写了出来。 ```java Arrays.asList(1 ,1 ,2 ,3 ,4 ,5 ,6 ).stream().min(Integer::min); Arrays.asList(1 ,1 ,2 ,3 ,4 ,5 ,6 ).stream().min(Integer::max); Arrays.asList(1 ,1 ,2 ,3 ,4 ,5 ,6 ).stream().distinct();
Match类方法
match类方法返回一个boolean值。
allMatch:Stream 中全部元素符合传入的 predicate,返回 true
anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 numList.stream().noneMatch(a -> a > 0 ); numList.stream().allMatch(a -> a > 0 ); numList.stream().anyMatch(a -> a > 0 ); ``` <h4>3. 关闭一个流(将流转化为其他数据结构)</h4> 当我们对一个流进行了足够的操作之后,希望将其转换为数据,List等数据结构方便存储。Stream在转换为其他数据结构的时候也是极其方便的。 ```java numList.stream().collect(Collectors.toList()); numList.stream().collect(Collectors.toSet()); numList.stream().toArray(); numList.stream().toString();
结束语
stream的基本用法到这里就差不多啦,以后有时间的话将自己使用的一些具体栗子逐渐补充一下,毕竟有栗子我们看起来总是更加容易懂一些。
最后!让我再来引用来自IBM的Stream API 详解中的结束语来结束这篇写了好几天的文章吧。
Stream 的特性可以归纳为:
不是数据结构
它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
所有 Stream 的操作必须以 lambda 表达式为参数
不支持索引访问
你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。不过请参阅下一项。
很容易生成数组或者 List
惰性化
很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。
Intermediate 操作永远是惰性化的。
并行能力
当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
可以是无限的
集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。
参考文章:
Java 8 中的 Streams API 详解
ChangeLog
2018-03-18 完成
**以上皆为个人所思所得,如有错误欢迎评论区指正。**
欢迎转载,烦请署名并保留原文链接。
联系邮箱:huyanshi2580@gmail.com
更多学习笔记见个人博客——>呼延十