Stream流式编程思想
--洱涷
(1)什么是Stream
Stream是一种流,是一种抽象的处理数据的思想,这种编程方式将要处理的元素集合看作一种流,流在管道中传输,然后在管道的每一个节点上对流进行操作(去重,分组,过滤…),元素流在经过管道的操作后,最后由最终操作得到新的一个元素集合。
Stream是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象所形成的一个队列,Stream并不会去储存元素,而是按照需求所进行元素计算等操作
- 数据源流的来源可以是集合、数组、产生器generator等
- 聚合操作就是类似于SQL语句的操作,filter,map,reduce,find,match,sorted等。
和Collection操作不同,Stream还具有两个基础的特征:
-
Pipelining:中间的每一次操作返回的都是流对象的本身,这样多个操作就可以串联成一个管道。这样做可以对操作进行优化,比如延迟执行(laziness)和短路(short-circuiting)
延迟执行:Stream的很多操作都是延迟执行的,例如,“查找具有三个连续元音的第一 个字符串”
短路:传入一个谓词,返回值为Boolean,如果符合条件直接结束流,例如: findFirst(),只要找到第一个符合的,直接结束
-
内部迭代:以前对集合遍历都是通过Iterator或for-each的方式,显式的在集合外部进行迭代,这叫外部迭代,Stream提供了内部迭代的方式,通过访问者模式实现。
(2)生成流
- stream() − 为集合创建串行流。
- parallelStream() − 为集合创建并行流。
串行流就是按照顺序执行一直到结束,并行流就是把内容切成多个数据块,并且利用多个线程分别处理每个数据块的内容。Stream的API中声明可以通过parallel()和sequential()方法在并行流和串行流之间切换。
注意:使用并行流并不一定能提高效率,因为jvm对数据进行切片和切换线程也是需要时间的,所以数据量越小,串行流越快,数据量越大,并行流效率越高。
JDK1.8并行流使用的是fork/join框架进行并行操作。
fork/join框架:就是在必要的情况下,将一个大任务拆分成若干个小任务(拆到不可再拆),再将一个个小任务的结果进行join汇总
(3)stream流的各种方法
1. count、anyMatch、allMatch、noneMatch
count方法和list的size()一样,返回的都是这个集合流的元素的长度,不同的一点,流是集合的一个高级工厂,中间操作是工厂里的每一道工序,我们对这个流操作完后,可以进行元素的数量和。
anyMatch表示判断的条件里,只要有一个成功,就返回true。
allMatch表示判断的条件里,所有的都要成功才会返回true。
noneMatch表示判断的条件里,所有的都不是返回true
List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
boolean aa = strs.stream().anyMatch(str -> str.equals("a"));
boolean bb = strs.stream().allMatch(str -> str.equals("a"));
boolean cc = strs.stream().noneMatch(str -> str.equals("a"));
long count = strs.stream().filter(str -> str.equals("a")).count();
System.out.println(aa);// TRUE
System.out.println(bb);// FALSE
System.out.println(cc);// FALSE
System.out.println(count);// 4
2. map、filter、flatMap
map方法:
这个方法传入了一个function函数式接口(定义了一个apply的抽象方法,接收一个泛型T对象,并且返回泛型R对象,我们在做基础数据处理的时候(eg: Integer i=0; Integer dd= i+1;),会对基础类型的包装类,进行拆箱的操作,转成基本类型,再做运算处理,拆箱和装箱,其实是非常消耗性能的,尤其是在大量数据运算的时候;这些特殊的Function函数式接口,根据不同的类型,避免了拆箱和装箱的操作,从而提高程序的运行效率)
function接口的应用例子:
Function<Integer, Integer> function1 = x -> x * 2;
System.out.println(function1.apply(4));// 8
Function<Integer, String> function2 = x -> x * 2 + "dd";
System.out.println(function2.apply(4));//8dd
Function<String, String> strFunction1 = (str) -> new String(str);
System.out.println(strFunction1.apply("aa"));//aa
Function<String, String> strFunction2 = String::new;
System.out.println(strFunction2.apply("bb"));//bb
Function<String, Emp> objFunction1 = (str) -> new Emp(str);
System.out.println(objFunction1.apply("cc").getName());//cc
Function<String, Emp> objFunction2 = Emp::new;
System.out.println(objFunction2.apply("dd").getName());//dd
这个接口接收一个泛型T,返回一个泛型R,表示调用这个方法后,可以改变返回的类型,操作如下:
Integer[] dd = {
1, 2, 3 };
Stream<Integer> stream = Arrays.stream(dd);
stream.map(str -> Integer.toString(str)).forEach(str -> {
System.out.println(str);// 1 ,2 ,3
System.out.println(str.getClass());// class java.lang.String
});
List<Emp> list = Arrays.asList(new Emp("a"), new Emp("b"), new Emp("c"));
list.stream().map(emp -> emp.getName()).for