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.关闭一个流
java8提供了多种构造流的方法
创建流的示例代码如下:
List<String> strList = new ArrayList<>();
strList.add("HuHanShi");
strList.add("HuBlanker");
String[] strings = {"HuHanShi", "HuBlanker"};
//Collection
Stream<String> stream = strList.stream();
stream = strList.parallelStream();
//数组
stream = Arrays.stream(strings);
stream = Stream.of(strings);
//BufferedReader
stream = new BufferedReader(new InputStreamReader(System.in)).lines();
//静态工厂
IntStream streamInt = IntStream.range(0, 10);
//自己生成流如果有需要放到最后面说
//其他:随机生成一个int的流
streamInt = new Random().ints();
//其他:按照给定的正则表达式切割字符串后得到一个流
stream = Pattern.compile(",").splitAsStream("wo,w,wa,a");
stream.forEach(System.out::println);//输出结果为:wo w wa a
当把数据结构包装成流之后,就要开始对里面的元素进行各种操作了。 流的操作分为两种: Intermediate:这类型的方法不会修改原来的流,而是会返回一个新的流以便后续操作。例如:map,filter,sorted. Terminal:这类型的方法会真正的将流进行遍历,在使用过后,流也将会被“消耗”,无法继续操作。 接下来将对常用的(我看过的)流的操作方法一一举例说明:
map()
对当前的流进行一个操作并将得到的结果包装成一个新的流返回。
//str是个字符串列表,将其转换成大写。
strList.stream().map(String::toUpperCase).collect(Collectors.toList());
flatMap()
flatMap与map的区别是他会将stream内部的结构扁平化,对每一个值都将其转化为一个流,最后将所有刘扁平化为一个流返回。
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。
//留下包含“Hu”的字符串
strList.stream().filter(perStr -> perStr.contains("Hu")).forEach(System.out::print);
forEach()
forEach 方法接收一个 Lambda 表达式,然后在 Stream 的每一个元素上执行该表达式。 forEach是Terminal操作,当遍历完成时,流被消耗无法继续对其进行操作。
错误示例:
//上面的几个示例中用到了forEach来进行打印操作,所以只举一下错误的例子。
//!这句话是错误的,当forEach之后无法再进行map操作。
strList.stream().forEach(System.out::print).map();
peek()
有人要问了,我想对每一个元素进行操作一下但是后续还要用怎么办呢,当然是有办法的,那就是peek()方法。
//先将strList中的字符串打印一遍之后将其转换为大写。
strList.stream().peek(System.out::print).map(perStr->perStr.toUpperCase()).collect(Collectors.toList());
reduce()
这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。
Integer sum = moreStream.flatMap(Collection::stream).reduce(0,(a,b)->a+b);
这个例子将flatMap中的结果进行了累加操作。 reduce()还可以用与字符串连接,求最大最小值等等。
sorted()
对stream中的值进行排序。
strList.sort(String::compareTo);
相比于数组的排序,stream的排序可以先剔除掉一些不需要排序的值,可以减少无用操作。
接下来将一些原理(类型)差不多的放一起说一哈。
limit()/skip()
取前n个元素/跳过前n个元素。
//对strList先取前两个再扔掉第一个然后打印,结果为:HuBlanker。
strList.stream().limit(2).skip(1).forEach(System.out::print);
findFirst()
取stream得第一个值,值得一提的是返回值为Optional<>. Optional是一个容器,可以包含一个值,使用它可以尽量避免NullPointException。 具体见:java8 Optional 类初体验
String first = strList.stream().findFirst().get();
min()/max()/distinct()
取最小/最大/无重复值。 min和max操作可以通过reduce方法实现,但是因为经常使用所以单独写了出来。
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值。
numList.stream().noneMatch(a -> a > 0);
numList.stream().allMatch(a -> a > 0);
numList.stream().anyMatch(a -> a > 0);
当我们对一个流进行了足够的操作之后,希望将其转换为数据,List等数据结构方便存储。Stream在转换为其他数据结构的时候也是极其方便的。
//List
numList.stream().collect(Collectors.toList());
//Set
numList.stream().collect(Collectors.toSet());
//Array
numList.stream().toArray();
//String
numList.stream().toString();
stream的基本用法到这里就差不多啦,以后有时间的话将自己使用的一些具体栗子逐渐补充一下,毕竟有栗子我们看起来总是更加容易懂一些。 最后!让我再来引用来自IBM的Stream API 详解中的结束语来结束这篇写了好几天的文章吧。
Stream 的特性可以归纳为:
2018-03-18 完成
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。