前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >流式操作真香啊

流式操作真香啊

作者头像
java后端指南
发布2021-05-13 16:53:02
5250
发布2021-05-13 16:53:02
举报
文章被收录于专栏:java后端java后端

今日主题:流式操作

简介

现在来看看流式操作吧,JDK8的流式操作是真的香啊,你还不会你就out

环境

  • JDK8

介绍

jdk8引进来的lambda语法是新语法,里面融进了python和c#的部分语法,stream()函数是主要用于集合而言的,对于数组就没效果

学习Stream之前,首先要了解他的操作符包含中间操作符终止操作符

中间操作符

对于数据流来说,中间操作符在执行制定处理程序后,数据流依然可以传递给下一级的操作符。

中间操作符包含8种(排除了parallel,sequential,这两个操作并不涉及到对数据流的加工操作):

map(mapToInt,mapToLong,mapToDouble) 转换操作符,把比如A->B,这里默认提供了转int,long,double的操作符。flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 变成 2,3,4 也就是从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作符。limit 限流操作,比如数据流中有10个 我只要出前3个就可以使用。distint 去重操作,对重复元素去重,底层使用了equals方法。filter 过滤操作,把不想要的数据过滤。peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等。skip 跳过操作,跳过某些元素。sorted(unordered) 排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。

终止操作符

数据经过中间加工操作,就轮到终止操作符上场了;终止操作符就是用来对数据进行收集或者消费的,数据到了终止操作这里就不会向下流动了,终止操作符只能使用一次。

collect 收集操作,将所有数据收集起来,这个操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以说Stream 的核心在于Collectors。count 统计操作,统计最终的数据个数。findFirst、findAny 查找操作,查找第一个、查找任何一个 返回的类型为Optional。noneMatch、allMatch、anyMatch 匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值。min、max 最值操作,需要自定义比较器,返回数据流中最大最小的值。reduce 规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。forEach、forEachOrdered 遍历操作,这里就是对最终的数据进行消费了。toArray 数组操作,将数据流的元素转换成数组。

这里只介绍了Stream,并没有涉及到IntStream、LongStream、DoubleStream,这三个流实现了一些特有的操作符,我将在后续文章中介绍到。

说了这么多,只介绍这些操作符还远远不够;俗话说,实践出真知。那么,Let‘s go。

1、Stream 流概念

Stream 流的使用总是按照一定的步骤进行,可以抽象出下面的使用流程。

代码语言:javascript
复制
数据源(source) -> 数据处理/转换(intermedia) -> 结果处理(terminal )

1.1、数据源

数据源(source)也就是数据的来源,可以通过多种方式获得 Stream 数据源,下面列举几种常见的获取方式。

代码语言:javascript
复制
Collection.stream(); 从集合获取流。
Collection.parallelStream(); 从集合获取并行流。
Arrays.stream(T array) or Stream.of(); 从数组获取流。
BufferedReader.lines(); 从输入流中获取流。
IntStream.of() ; 从静态方法中获取流。
Stream.generate(); 自己生成流

1.2、数据处理

数据处理/转换(intermedia)步骤可以有多个操作,这步也被称为intermedia(中间操作)。在这个步骤中不管怎样操作,它返回的都是一个新的流对象,原始数据不会发生任何改变,而且这个步骤是惰性计算处理的,也就是说只调用方法并不会开始处理,只有在真正的开始收集结果时,中间操作才会生效,而且如果遍历没有完成,想要的结果已经获取到了(比如获取第一个值),会停止遍历,然后返回结果。惰性计算可以显著提高运行效率。

代码语言:javascript
复制
 //将数组转成集合
        List<String> nameList= Arrays.asList("aaf","bbccdd","cdsc","dsd");

        /*1、筛选名字长度为4的
        * 2、名字前面拼接This is
        * 3、遍历输出
        * */
        nameList.stream()
                .filter(name->name.length()==4)
                .map(name->"This is "+name)
                .forEach(name-> System.out.println(name));

数据处理/转换操作自然不止是上面演示的过滤 filter 和 map映射两种,另外还有 map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered 等。

1.3、收集结果

结果处理(terminal )是流处理的最后一步,执行完这一步之后流会被彻底用尽,流也不能继续操作了。也只有到了这个操作的时候,流的数据处理/转换等中间过程才会开始计算,也就是上面所说的惰性计算。结果处理也必定是流操作的最后一步。

常见的结果处理操作有 forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator 等。

代码语言:javascript
复制
//数组转成集合
        List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
        //将集合的元素变成大写的
        List<String> upperCaseName=nameList.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        upperCaseName.forEach(name-> System.out.println(name));

1.4、short-circuiting

有一种 Stream 操作被称作 short-circuiting ,它是指当 Stream 流无限大但是需要返回的 Stream 流是有限的时候,而又希望它能在有限的时间内计算出结果,那么这个操作就被称为short-circuiting。例如 findFirst 操作。

2、Stream流的使用

Stream 流在使用时候总是借助于 Lambda 表达式进行操作,Stream 流的操作也有很多种方式,下面列举的是常用的 11 种操作。

2.1、Stream流的获取

大概是以下几种获取流的方式

代码语言:javascript
复制
    public static void createStream() throws FileNotFoundException {
        //数组转集合
        List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
        //数组
        String[] nameArr = {"Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter"};
        //集合获取串行Stream流
        Stream<String> nameListStream=nameList.stream();
        //集合获取并行Stream流
        Stream<String> nameListStream2=nameList.parallelStream();
        //数组获取Stream流
        Stream<String> nameAttrStream=Stream.of(nameArr);
        //数组获取Stream流
        Stream<String> nameAttrStream2=Arrays.stream(nameArr);
        //文件流获取Stream流
        BufferedReader bufferedReader=new BufferedReader(new FileReader("1.txt"));
        Stream<String> linesStream=bufferedReader.lines();
        //从静态方法获取流操作
        IntStream rangeStream=IntStream.range(1,10);
        rangeStream.limit(10).forEach(num-> System.out.println(num+","));
        
    }

2.2、forEach

遍历

代码语言:javascript
复制
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numberList.stream().forEach(number -> System.out.println(number+","));

2.3、map/ flatMap

使用 map 把对象一对一映射成另一种对象或者形式。相当于:key是原来的值,value是处理之后的值 map是一对一映射

代码语言:javascript
复制
 //数组转集合
        List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        //映射成2倍数字
        List<Integer> collect=numberList.stream().map(num->num*2).collect(Collectors.toList());
        collect.forEach(num-> System.out.println(num));

有时候会出现一对多的映射,这时候就用flatmap

2.4、filter

使用 filter 进行数据筛选,挑选出想要的元素,下面的例子演示怎么挑选出偶数数字。

代码语言:javascript
复制
 //数组转集合
        List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        //筛选偶数数字
        List<Integer> collect=numberList.stream().filter(num->num%2==0).collect(Collectors.toList());
        collect.forEach(num-> System.out.println(num));

2.5、findFirst

findFirst 可以查找出 Stream 流中的第一个元素,它返回的是一个 Optional 类型

代码语言:javascript
复制
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        Optional<Integer> firstNumber=numberList.stream().findFirst();
        //orElse表示如果结果为空就用-1表示
        System.out.println(firstNumber.orElse(-1));

findFirst 方法在查找到需要的数据之后就会返回不再遍历数据了,也因此 findFirst 方法可以对有无限数据的 Stream 流进行操作,也可以说 findFirst 是一个 short-circuiting 操作。

2.6、collect / toArray

Stream 流可以轻松的转换为其他结构

代码语言:javascript
复制
//stream转换为其他数据结构
        List<Integer> numberList=Arrays.asList(1,2,3,4,5);
        //to array
        Integer[] toArray=numberList.stream().toArray(Integer[]::new);
        System.out.println(toArray);
        //to List
        List<Integer> integerList=numberList.stream().collect(Collectors.toList());
        System.out.println(integerList);
        //to set
        Set<Integer> integerSet=numberList.stream().collect(Collectors.toSet());
        System.out.println(integerSet);
        //to String
        String toString=numberList.stream().map(num->String.valueOf(num)).collect(Collectors.joining());
        System.out.println("toString:"+toString);
        //to String split by ,
        String toString2=numberList.stream().map(num->String.valueOf(num)).collect(Collectors.joining(","));
        System.out.println("toString2:"+toString2);

2.7、limit / skip

limit:获取前面多少个元素 skip:跳过前面多少个元素

代码语言:javascript
复制
// 生成自己的随机数流
        List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
        //取前面前3个
        ageList.stream().limit(3).forEach(System.out::println);
        //跳过前面3个
        ageList.stream().skip(3).forEach(System.out::println);

2.8、Statistics

数学统计功能,求一组数组的最大值、最小值、个数、数据和、平均数等。

代码语言:javascript
复制
//数学计算测试
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        //mapToInt是将元素映射成int类型
        IntSummaryStatistics stats=list.stream().mapToInt(x->x).summaryStatistics();
        System.out.println("最大值:"+stats.getMax());
        System.out.println("最小值:"+stats.getMin());
        System.out.println("个数:"+stats.getCount());
        System.out.println("和:"+stats.getSum());
        System.out.println("平均数:"+stats.getAverage());

2.9、groupingBy

分组聚合功能,和数据库的 Group by 的功能一致。

代码语言:javascript
复制
List<Integer> ageList=Arrays.asList(11,12,13,22,23,24);
        Map<String,List<Integer>> ageGroupMap=ageList.stream().collect(Collectors.groupingBy(age->String.valueOf(age/10)));
        ageGroupMap.forEach((k,v)->{
            System.out.println("年龄:"+k+"0多岁的有:"+v);
        });

2.10、partitioningBy

按某个条件分组 给一组年龄,分出成年人和未成年人

代码语言:javascript
复制
//按某个条件分组,给一组年龄,分出成年人和未成年人
        List<Integer> ageList=Arrays.asList(12,23,45,17,58,35);
        Map<Boolean,List<Integer>> ageMap=ageList.stream().collect(Collectors.partitioningBy(age->age>18));
        System.out.println("未成年人:"+ageMap.get(false));
        System.out.println("成年人:"+ageMap.get(true));

2.11、进阶 - 自己生成 Stream 流

代码语言:javascript
复制
// 生成自己的随机数流
        Random random = new Random();
        Stream<Integer> generateRandom = Stream.generate(random::nextInt);
        generateRandom.limit(5).forEach(System.out::println);
        // 生成自己的 UUID 流
        Stream<UUID> generate = Stream.generate(UUID::randomUUID);
        generate.limit(5).forEach(System.out::println);

3、并行计算

获取 Stream 流时可以使用 parallelStream 方法代替 stream 方法以获取并行处理流,并行处理可以充分的发挥多核优势,而且不增加编码的复杂性。

代码语言:javascript
复制
/**
  * 并行计算
  */
 @Test
 public void main() {
     // 生成自己的随机数流,取一千万个随机数
     Random random = new Random();
     Stream<Integer> generateRandom = Stream.generate(random::nextInt);
     List<Integer> numberList = generateRandom.limit(10000000).collect(Collectors.toList());

     // 串行 - 把一千万个随机数,每个随机数 * 2 ,然后求和
     long start = System.currentTimeMillis();
     int sum = numberList.stream()
         .map(number -> number * 2)
         .mapToInt(x -> x)
         .sum();
     long end = System.currentTimeMillis();
     System.out.println("串行耗时:"+(end - start)+"ms,和是:"+sum);

     // 并行 - 把一千万个随机数,每个随机数 * 2 ,然后求和
     start = System.currentTimeMillis();
     sum = numberList.parallelStream()
         .map(number -> number * 2)
         .mapToInt(x -> x)
         .sum();
     end = System.currentTimeMillis();
     System.out.println("并行耗时:"+(end - start)+"ms,和是:"+sum);
 }

结果:

代码语言:javascript
复制
串行耗时:1005ms,和是:481385106
并行耗时:47ms,和是:481385106
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java后端指南 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 今日主题:流式操作
    • 简介
      • 环境
        • 介绍
        • 中间操作符
        • 终止操作符
          • 1、Stream 流概念
            • 1.1、数据源
            • 1.2、数据处理
            • 1.3、收集结果
            • 1.4、short-circuiting
          • 2、Stream流的使用
            • 2.1、Stream流的获取
            • 2.2、forEach
            • 2.3、map/ flatMap
            • 2.4、filter
            • 2.5、findFirst
            • 2.6、collect / toArray
            • 2.7、limit / skip
            • 2.8、Statistics
            • 2.9、groupingBy
            • 2.10、partitioningBy
            • 2.11、进阶 - 自己生成 Stream 流
          • 3、并行计算
          相关产品与服务
          GPU 云服务器
          GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于生成式AI,自动驾驶,深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档