java8 不会就out了(二)Stream流详解(使用+原理)

Stream

List<String> deviceNames = deviceList
                             .stream()
                             .filter(x->{x.getId()<100})
                             .map(EnvironmentDeviceDTO::getDeviceName)
                             .distinct()
                             .collect(Collectors.toList());

概述:一个对集合处理十分方便的工具。
将要处理的元素集合看作一种流,借助Stream API 对流中的元素进行操作,如筛选、排序、聚合等。

操作类型

Stream 可以由数组或者集合创建,对流的操作可以分为两种:

  1. 中间操作,以管道的形式,每次操作后返回一个新的流,可以连续、多个使用。
  2. 终端操作,每个流的结束操作,结果返回一个新的集合或者值。

其实最开始对stream流的了解就到这里了,我也以为够用了,不就是像写SQL一样,从逻辑上对集合不断约束就行了吗,一步一步执行,直到我看到这个例子:

IntStream.range(1, 10)
   .peek(x -> System.out.print("\nA" + x))
   .limit(3)
   .peek(x -> System.out.print("B" + x))
   .forEach(x -> System.out.print("C" + x));

执行结果: A1B1C1 A2B2C2 A3B3C3
而按我最开始的思路应该是 A1A2A3A4…A10 B1B2B3 C1C2C3

就发现事情没有我想的那么简单,所以还得接着往下探究。

操作状态(对于理解原理很重要)

中间操作:有状态 | 无状态

有状态的中间操作会等待前序操作结束才会执行,如sort(),得等前序所有元素执行完事才排序

无状态的中间操作会直接执行,与前序无关,如map()

结束操作:非短路操作 | 短路操作

非短路操作会等待所有元素处理完才返回,如collect()

短路操作满足条件就返回了,如findFirst()

特性:

  1. stream不会储存数据,只是对原有数据源做计算操作。
  2. stream具有惰性计算的特点,只有当执行终端操作时才会执行前面的中间操作。
  3. 由第一条可接着分析:stream不是数据结构,它没有内部储存,只是通过操作《管道》从源数据中抓取元素,

从原理发掘

code:

int longestStringLengthStartingWithA
        = strings.stream()
              .filter(s -> s.startsWith("A"))
              .mapToInt(String::length)
              .max();
int longest = 0;
for(String str : strings){
    if(str.startsWith("A")){// 1. filter(), 保留以A开头的字符串
        int len = str.length();// 2. mapToInt(), 转换成长度
        longest = Math.max(len, longest);// 3. max(), 保留最长的长度
    }
}
  1. 并不是每执行一次函数就进行一次迭代(其实我了解之前就是这样以为的。。其实如果是这样的话效率其实十分低下,比如filter+map+foreach,就会有三次迭代,而其实自己实现功能只会有一次迭代),正确的思路是用一种方式记录用户每一步的操作,当用户调用结束操作时将记录的操作叠加到一次迭代中全部执行掉。
  2. 记录操作:调用一系列的中间操作,不断的产生新的Stream,这些stream通过双向链表的形式链接在一起,构成流水线。
  3. 操作如何叠加:给所有的操作(stage)都实现同一个接口《SINK》,约定好执行方案,每一个操作只需负责自己的处理->转发,那么对于每一个操作可能就这样处理了:
void accept(U u){
    1. 使用当前Sink包装的回调函数处理u
    2. 将处理结果传递给流水线下游的Sink
}
  1. 结束操作就是调用链的出口。

创建方式

  1. 通过集合中的stream()方法创建
public static void methodOne(){
    List<String> list = Arrays.asList("11","33","22");
    Stream<String> stream = list.stream(); // 串行
    Stream<String> stringStream = list.parallelStream(); // 并行流
    stream.forEach(System.out::println);
}
  1. 通过Arrays中的stream()方法创建
public static void methodTwo(){
    int[] array = {1,2,4,5,6,7,1};
    IntStream stream = Arrays.stream(array);
    stream.forEach(System.out::println);
}
  1. 通过Stream中的静态方法创建(of,iterate,generate)
public static void methodThr(){
    Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
    integerStream.forEach(System.out::println);
    Stream<Integer> iterate = Stream.iterate(0, x -> x + 1).limit(10);
    Stream<Double> generate = Stream.generate(Math::random).limit(10);
    iterate.forEach(System.out::println);
    generate.forEach(System.out::println);
}

并行流和串行流的区别,并行流是内部以多线程进行的,需要小心点是,要确保执行的任务对线程环境没有要求,如果任务量并不大建议不要使用,同事并行流不能保证处理结果的顺序。

常用的操作

中间操作方法分类:(高亮为无状态操作,正常为有状态操作)

  • filter() 筛选
  • map() 映射
  • flatMap() 扁平化映射,将流中的每个值都换成另一个流,然后把所有流连接成一个流
  • distinct() 去重
  • sorted() 排序
  • peek() 可以理解为没有返回值的map,用于对流进行操作
  • limit() 限定多少个
  • skip() 跳过

终端操作方法分类:(高亮为短路操作,正常为非短路操作)

  • forEach() 遍历
  • forEachOrdered() 并行时保证顺序遍历
  • toArray()
  • reduce(0,x,y->x+y) 规约,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作
  • collect() 收集,把流收集成一个值,或者集合(还有分组,拼接)
  • min() 求最小值啦
  • max() 最大值
  • count() 个数
  • anyMatch() 匹配到没有
  • allMatch()
  • noneMatch()
  • findFirst() 匹配获取值
  • findAny()
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值