Stream流

参考:强大的Stream API以及并行流与串行流

JDK8-Stream并行流详解

强大的Stream并行流

Java 8 (6) Stream 流 - 并行数据处理与性能

Fork/Join框架详解

Stream概述

stream是JDK8中处理集合的关键抽象概念,可以对集合进行操作,可以执行复杂的查找、过滤和映射数据等操作。stream API提高一种高效且易于使用的处理数据的方式。

stream流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

特点:

  • stream自己不会存储元素
  • stream不会改变源对象
  • stream操作是延迟执行的。会等到需要结果的时候执行。

操作步骤:

  1. 创建stream流
  2. 中间操作。这是一个中间操作链,对数据源进行处理,包含filter、map、limit等
  3. 终止操作。一个终止操作,才执行中间操作链,并产生结果。

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框架

把大任务分割成若干个小任务,最终汇总每一个小任务结果后得到大任务结果的框架。

image-20210223121533601

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。

posted @ 2021-02-23 12:21  Jayzou11223  阅读(645)  评论(0)    收藏  举报