Java8新特性之Stream

Java8新特性之Lambda

大家回忆下日常学习工作中使用的最多的 Java API 是是什么?相信很多人的答案和我一样都是集合。我们选择适合的集合数据结构存储数据,而我们之于集合最多的操作就是遍历,实现查询,统计,过滤,合并等业务。

哪里用Stream 集合遍历

外部迭代:通过 for循环,Iterator迭代器遍历集合,手动的拿到集合中每个元素进行相应处理

优点

对于程序的掌控更高

性能强(如果算法功力深厚)

缺点

很多重复的模板代码

需要很多中间临时变量来减少遍历次数

性能完全取决于程序员水平,烧脑

代码不易读

容易出错:例如for循环遍历LinkedList会出错

内部迭代:只提供对集合中元素的处理逻辑,遍历过程交给库类,Java5提供了foreach,Java8提供了Stream

优点

代码好读

简单,只需要提供处理逻辑

缺点

有些情况性能比外部迭代差一点点

在使用foreach时不能对元素进行赋值操作

为什么要Stream

本文要介绍的Stream属于内部迭代,之前我们已经有了foreach减少了我们的代码量,为什么我们还需要Stream呢?

流水线的方式处理集合,结合Lambda爽歪歪

代码超短超好读

Stream的开始到结束就相当于一次遍历,允许我们在遍历中拼接多个操作

强调的是对集合的计算逻辑,逻辑可以多次复用

特别繁重的任务可以很轻松的转为并行流,适合类似于大数据处理等业务

成本非常小的实现并行执行效果

怎么用Stream

上文我们大概知道了Stream主要服务与集合,流的过程可以总结为三步:

开始操作:读取数据源(如集合)

中间操作:组装中间操作链,形成一条流的流水线

终端操作:一次迭代执行流水线,结束流,并生成结果

中间操作和终端操作

第一步流加载集合数据,库类完成我们不需要关心,要想使用好Stream有必要从不同维度了解主要的操作

中间操作:流水线的部件,返回的是this,也就是Stream,此时迭代并没有执行

终端操作:流水线真正开始执行,返回的是处理结果,终端操作过后流关闭

无状态操作:例如对集合中所有元素做转换,或者过滤,不用保存别的元素的处理结果

有状态操作:例如排序,去重操作,需要保存之前集合中元素的状态

非短路操作:按部就班完成迭代,返回处理结果

短路操作:只有一达到预设的条件,立刻停止并返回

下图给出了Stream给出的一些常用api的基本信息

Java8新特性之Stream

基本数据类型流

为了避免自动装箱拆箱消耗性能,Stream为我们提供了IntStream、DoubleStream和LongStream,分别将流中的元素特化为int、long和double,这些特殊的流中提供了range,sum,max等数字类型常用的api

并行流

当我们面对计算是否密集的应用开发时,为了充分利用硬件资源,可以简单的通过改变parallel方法将流变成并行执行,但是在使用时有如下注意事项。

并行流是通过 fork/join 线程池来实现的,该池是所有并行流共享的。默认情况,fork/join 池会为每个处理器分配一个线程。假设你有一台16核的机器,这样你就只能创建16个线程。而与此同时其他任务将无法获得线程被阻塞住,所以使用并行流要结合机器和业务场景。

避免在并行流中改变共享状态,小心使用有状态的操作

并行流并不一定就快,要将多个线程的执行结果汇总

实战

相信大家最关心的还是实际开发中能帮助我们解决哪些问题,通过一些简单案例熟悉各种操作的用法

开始操作 值生成流 // 生成空流 Stream<Object> empty = Stream.empty(); // 值生成流 Stream<String> stringStream = Stream.of("1", "2", "3"); 数组生成流 String[] strings = { "1", "2", "3"}; Stream<String> stream = Arrays.stream(strings); 集合生成流 List<String> strings1 = Arrays.asList("1", "2", "3"); Stream<String> stream1 = strings1.stream(); 文件生成流 Stream<String> lines = Files.lines(Paths.get("/c/mnt/")); 函数生成流(无限流) // 无限流,流从0开始,下面的每个元素依次加2 Stream<Integer> iterate = Stream.iterate(0, num -> num + 2); // 无限流,流中每个元素都是 0~1 随机数 Stream<Double> generate = Stream.generate(Math::random); 数值范围生成流 // 生成0到10的int流 IntStream intStream = IntStream.rangeClosed(0, 10); // 生成0到9的int流 IntStream intStream1 = IntStream.range(0, 10); 手动生成流 // 生成有字符串a和数字1的异构的流 Stream.builder().add("a").add(1).build().forEach(System.out::print); 合并两个流 Stream.concat( Stream.of("1", 22, "333"), Stream.of("1", 22, 333) ).forEach(System.out::print); 中间操作

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygjpd.html