java8Stream

Stream

 

java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。

同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。

通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

 

Stream 总览

什么是流

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

 

而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

 

Stream 的另外一大特点是,数据源本身可以是无限的。

 

流的操作类型分为两种:

  • Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。

  • Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

 

 

在对于一个 Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream 的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。

 

流的使用详解

简单说,对 Stream 的使用就是实现一个 filter-map-reduce 过程,产生一个最终结果,或者导致一个副作用(side effect)。

 

流的构造与转换

下面提供最常见的几种构造 Stream 的样例。

清单 4. 构造流的几种常见方法

Stream stream = Stream.of("a", "b", "c"); 

String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);

List<String> list = Arrays.asList(strArray);
stream = list.stream();

Stream<String> streamBuilder = Stream.<String>builder().add("a").add("b").add("c").build();

 

 

Intermediate操作

map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

 

map、filter、peek、distinct

//示例 去除列表的列表中的空数据
  List<List<String>> ll = Lists.newArrayList();
    for (int i = 0; i < 10; i++) {
      List<String> l = Lists.newArrayList();
      l.add(" ");
      l.add("a");
      l.add("b");
      ll.add(l);
    }
    List<String> firstL2 = Lists.newArrayList();
    firstL2.add("");
    ll.add(firstL2);

/**map 主要功能:转化  将x的某一个类型转化为另外一个“东西” 可以是同类型也可以是不同类型 再return出来 (这里省略了return)**/
  List<List<String>> llmapStream = ll.stream()
        .map(x -> x.stream().filter(StringUtils::isNotBlank).collect(Collectors.toList()))
        .filter(CollectionUtils::isNotEmpty).collect(Collectors.toList());
 
//peek 主要功能:操作 将x进行某个没有返回值的操作 可以输出日志等等
List<List<String>> llPeekStream = ll.stream().peek(x -> x.removeIf(StringUtils::isBlank))
        .filter(CollectionUtils::isNotEmpty).collect(Collectors.toList());

//filter 主要功能:判断 对x进行判断,满足条件的继续存在在stream中
省略

flatMap:将几个小的list合并成一个大的List

{{1,2},{3,4},{5,6}}  - > flatMap  - > {1,2,3,4,5,6}

limit获取前几个元素 

skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream

parallel:并行操作

 

Terminal操作

forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

 

reduce:

/**reduce 主要功能:聚合计算 可以接受一个、两个或者三个参数 
一个参数 表示stream前一个后一个  返回是一个Optional对象
两个参数 表示有初始值 然后前一个后一个 返回是一个初始值相同的具体类型 
三个参数 parallelStream 并行流下才会执行第三个参数  这个和前两个优点不同 第二个参数中的第一个参数为初始值,第二个参数list中遍历的值,第二个参数运算的结果作为第三个参数的值,第三个参数相当于把第二个参数运算的结果又reduce了一次
**/


    Stream<Integer> s = Stream.of(1, 2, 3);
    Integer reduce1 = s.reduce((a,b)->a+b).get();
    System.out.println("reduce1:"+reduce1);

    Stream<Integer> s2 = Stream.of(1, 2, 3);
    Integer reduce2 = s2.reduce(10,(a,b)->{

      String threadName = Thread.currentThread().getName();
      System.out.println("reduce2,a:"+a.toString());
      System.out.println("reduce2,b:"+b.toString());

      return  a+b;

    });
    System.out.println("reduce2:"+reduce2);

    List<Integer> s3 = Lists.newArrayList();
    s3.add(1);
    s3.add(2);
    s3.add(3);

// 三个参数

    Integer reduce3 = s3.parallelStream().reduce(100,(a,b)->{

      String threadName = Thread.currentThread().getName();
      System.out.println("reduce3,a:"+a.toString());
      System.out.println("reduce3,b:"+b.toString());

      return  a+b;

  },(u,t)-> {
      String threadName = Thread.currentThread().getName();
      System.out.println("tname:"+threadName+",u:"+u.toString());
      System.out.println("tname:"+threadName+",t:"+t.toString());

    return u+t;
    });
    System.out.println("reduce3:"+reduce3);
  }

/**三个参数的输出结果
reduce3,a:100
reduce3,b:3
reduce3,a:100
reduce3,a:100
reduce3,b:1
reduce3,b:2
tname:main,u:102
tname:main,t:103
tname:main,u:101
tname:main,t:205
reduce3:306
**/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值