Stream流
Java 8 (6) Stream 流 - 并行数据处理与性能
Stream概述
stream是JDK8中处理集合的关键抽象概念,可以对集合进行操作,可以执行复杂的查找、过滤和映射数据等操作。stream API提高一种高效且易于使用的处理数据的方式。
stream流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
特点:
- stream自己不会存储元素
- stream不会改变源对象
- stream操作是延迟执行的。会等到需要结果的时候执行。
操作步骤:
- 创建stream流
- 中间操作。这是一个中间操作链,对数据源进行处理,包含filter、map、limit等
- 终止操作。一个终止操作,才执行中间操作链,并产生结果。
Stream具体操作
1、创建stream流
- default Stream<E> stream():返回一个顺序流。
- default Stream<E> parallelStream():返回一个并行流。
2、中间操作
多个中间操作可以链式进行,形成流水线,只有当触发终止操作,中间操作才会执行。
中间操作返回的值还是stream流对象。
中间操作:
- 筛选与切片
filter:从流中排除元素
distinct:去重(通过hashcode和equals)
limit:截断,不超过给定数
skip:跳过元素
- 映射
map:接收一个函数作为参数,应用到每个元素上。
mapToDouble、mapToInt、mapToLong、flatMap等
- 排序
sorted:按自然排序,或者实现Comparator 接口,写比较器,自定义排序
3、终止操作
终止操作会从流的流水线生产结果。其结果可以是任何不是流的值,即终止操作返回值已经不再是stream流对象了。
终止操作:
- 查找与匹配
allMatch、anyMatch、noneMatch:匹配
findFirst、findAny:查找返回
count:元素综述
max:最大值
min:返回流中最小值
forEach: 内部迭代
注:流进行了终止操作后,就不能在使用了,会报错。
- 归约
reduce:将流中元素反复结合起来。
- 收集
collect:将流转换为其他形式。收集到list、set、map等。
stream并行流
并行流:把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
在JDK8在通过声明parallel和sequential在并行流和串行流之间切换。
对于并行流,沿用了JDK7提供的fork/join框架。
- 影响并行流性能的5个因素:
- 数据大小
- 源数据结构
- 装箱拆箱,尽量使用基本数据类型,避免装箱拆箱
- CPU核数
- 单元处理开销
 
1、fork/join框架
把大任务分割成若干个小任务,最终汇总每一个小任务结果后得到大任务结果的框架。

fork/join框架与传统线程池的区别:
- 采用工作窃取模式(work-stealing):
当执行新的任务时,它可以将其拆分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
- 传统线程池,如果一个线程正在执行的任务由于某些原因无法运行,那么该线程会处于等待状态。
- fork/join框架实现中,如果某个子问题由于等待另一个子问题完成而无法继续运行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行,减少线程的等待时间,提高性能。
2、案例分析
并行处理案例
// 使用一个容器装载 100 个数字,通过 Stream 并行处理的方式将容器中为单数的数字转移到容器 parallelList
List<Integer> integerList= new ArrayList<Integer>();
for (int i = 0; i <100; i++) {
      integerList.add(i);
}
List<Integer> parallelList = new ArrayList<Integer>() ;
integerList.stream()
           .parallel()
           .filter(i->i%2==1)
           .forEach(i->parallelList.add(i));
- 案例存在的问题
最后parallelList集合中的值可能为null值或者缺少值
- 原因
因为parallelList声明的是ArrayList,ArrayList是线程不安全的。所以在并行流,多线程下,在forEach循环遍历是会有问题。
例如在parallelList.add()时,线程1在size++后准备给index为size+1的位置赋值,这个时候线程2又来size++,这个线程1中赋值的index就变成了size+2,在线程1赋值后,线程2又在size+2位置赋值。最终结果就是size+1位置没有赋值,size+2位置是线程2的值,线程1的值被覆盖。
- 解决方法:
最佳:将forEach操作换为collect(),转为list。
其他方法:同步机制,加锁、vector。
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号