欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

stream的使用

程序员文章站 2022-06-16 10:18:31
stream的使用 概述 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation) 集合专注的是数据,流专注的是算法和计算(Stream不是集合元素、不是数据结构、不保存数据) Stream API 借 ......
stream的使用
  • 概述
    • stream 是对集合(collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation)
    • 集合专注的是数据,流专注的是算法和计算(stream不是集合元素、不是数据结构、不保存数据)
    • stream api 借助于同样新出现的 lambda 表达式,极大的提高编程效率和程序可读性。
    • 提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join并行方式来拆分任务和加速处理过程。
    • java 的并行 api 演变历程基本如下:
      • 1.0-1.4 中的 java.lang.thread
      • 5.0 中的 java.util.concurrent
      • 6.0 中的 phasers 等
      • 7.0 中的 fork/join 框架
      • 8.0 中的 lambda
    • stream 的另外一大特点是,数据源本身可以是无限的。
  • 特性
    • 无存储:stream是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。
    • 函数式编程:对stream的任何修改都不会修改背后的数据源,比如对sream的filter操作并不会删除被过滤掉的元素,而是生成一个不含被过滤元素的新的stream。
    • 延迟执行:stream由一个或多个中间操作(intermediate operation)和一个结束操作(terminal operation)两部分组成。只有执行了结束操作,stream定义的中间操作才会依次执行,这就是stream的延迟特性。
    • 可消费性:stream只能被“消费”一次,一旦遍历过就会失效。就像容器的迭代器那样,想要再次遍历必须重新生成一个新的stream。
  • stream类
    • 继承关系:stream ——》 basestream<t, s extendsbasestream<t, s>> ——》autocloseable
      • basestream还派生出基于三种基本数据类型的流:intstream, longstream, doublestream
      • 由于继承了autocloseable,便集成了流的自动关闭操作。
    • basestream详解
      • iterator iterator();    获得流的迭代器,并返回对该迭代器的引用(终端操作)
      • spliterator spliterator();   获取流的spliterator,并返回其引用(终端操作)
      • boolean isparallel();   如果调用流是一个并行流,则返回true;如果调用流是一个顺序流,则返回false。
      • s sequential();  基于调用流,返回一个顺序流。如果调用流已经是顺序流了,就返回该流。(中间操作)
      • s parallel();  基于调用流,返回一个并行流。如果调用流已经是并行流了,就返回该流。(中间操作)
      • s unordered();  基于调用流,返回一个无序流。如果调用流已经是无序流了,就返回该流。(中间操作)
      • s onclose(runnable closehandler);   返回一个新流,closehandler指定了该流的关闭处理程序,当关闭该流时,将调用这个处理程序。(中间操作)
      • void close();  从autocloseable继承来的,调用注册关闭处理程序,关闭调用流(很少会被使用到)
  • 流的操作
    • 中间操作(intermediate operations)
      • 一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),只有在指定的终端操作上才会执行中间操作。
      • 无状态操作
        • 处理流中的元素时,会对当前的元素进行单独处理。比如:谓词过滤操作,因为每个元素都是被单独进行处理的,所有它和流中的其它元素无关,因此被称为无状态操作。
      • 有状态操作
        • 某个元素的处理可能依赖于其他元素。比如查找最小值,最大值,和排序,因为他们都依赖于其他的元素。因此为称为有状态操作。
    • 终端操作(terminal operations)
      • 流只能有一个 terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
      • 短路操作(short-circuiting)
        • 对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的stream,但返回一个有限的新 stream。
        • 对于一个 terminal 操作,如果它接受的是一个无限大的 stream,但能在有限的时间计算出结果。
    • stream的使用
  • stream的使用
    • 1.创建stream:从数据源(如集合或数组)中获取一个流,或自行生成流
      • 通过数组或集合
      •         // 通过集合生成
                collection<string> collection = new arraylist<>(16);
                stream<string> stream = collection.stream();
                    // 并行流
                stream<string> stringstream = collection.parallelstream();
                // 通过数组生成
                int[] nums = new int[6];
                intstream stream1 = arrays.stream(nums);    
      • stream的静态工厂 或 builder模式
                // 通过流的静态工厂
                stream<integer> integerstream = stream.of(1, 2, 3);
                    //生成空流
                stream<integer> empty = stream.empty();
                    // 拼接流
                stream<integer> concat = stream.concat(integerstream, empty);
                    // 生成无限流, 并不会一开始生成无限个数,加入内存
                    // 而是通过终端操作, 内部迭代生成, 可以通过limit()截断
                stream<double> generate = stream.generate(math::random);
                stream<integer> iterate = stream.iterate(1, (x) -> x + 2);
                iterate.limit(5).foreach(system.out::println);
                    // intstream 或 longstream 的静态工厂
                    // 生成包含 1 到 4 的流, 不含结束值
                intstream.range(1, 5);
                    // 生成包含 1 到 5 的流,包含结束值
                intstream.rangeclosed(1, 5);
                // 流的builder
                stream.builder<object> builder = stream.builder();
                builder.accept(1);
                    // add方法返回当前builder对象,以实现链式编程
                builder.add(2).add(3);
                builder.build().foreach(system.out::println);
      • 其他
        • java.io.bufferedreader.lines()
        • java.nio.file.files.walk()
        • random.ints()
        • bitset.stream()
        • pattern.splitasstream(java.lang.charsequence)
        • jarfile.stream()
        • 自行构建:java.util.spliterator
    • 2.数据转换
      • 每次中间操作过程,不会修改原有stream对象,而生成一个新的stream返回,从而实现链式编程。
      • 映射
        • 将流中的元素映射为新的数据类型到新流中
        • map
                  // map()
                      // 将流中的元素进行处理,返回一个新的数据类型,映射到新流中
                  list<integer> list = new arraylist<>(arrays.aslist(1,2,3));
                  list.stream().map(i -> string.valueof(i) + "test").foreach(system.out::println);
                      // maptoint,maptolong,maptodouble 与map类似,只是映射为基本类型到对应的stream中
        • flatmap
                  //flatmap
                      //flatmap()操作能把原始流中的元素进行一对多的转换,并且将新生成的元素全都合并到它返回的流里面。
                  list<string> stringlist = new arraylist<>(arrays.aslist("1,2,3", "4,5,6", "7,8,9"));
                  stringlist.stream()
                          .flatmap(s -> arrays.stream(s.split(",")))
                          .foreach(system.out::print); //输出 : 123456789    
      • 遍历-peek()
        • peek的作用类似foreach(),但唯一不同的是peek不是终端操作,不会消费原有的流
    • 3.执行终端操作,返回结果
      • 缩减操作:把一个流缩减为一个值
        • 特性
          • 无状态:每个元素都被单独地处理,他和流中的其它元素是没有任何依赖关系的
          • 不干预:操作数不会改变数据源
          • 关联性:标准的数学含义,即,给定一个关联运算符,在一系列操作中使用该运算符,先处理哪一对操作数是无关紧要的
        • 特例缩减:max(),min(),count()
        • reduce()
                  // 求总和  reduce
                  // optional<t> reduce(binaryoperator<t> accumulator); 如果流为空,则返回空,因此返回optional
                  optional<integer> sum1 = stream.of(1, 2, 3).reduce(integer::sum);
                  if (sum1.ispresent()) {
                      system.out.println("sum : " + sum1.get()); // sum : 6
                  }
                  // t reduce(t identity, binaryoperator<t> accumulator);
                  // 第一个参数为 初始值,若流为空,则返回初始值,因此返回值不可能为空
                  integer sum2 = stream.of(1, 2, 3).reduce(1, integer::sum);
                  system.out.println("sum : " + sum2); //  sum : 7
                  // <u> u reduce(u identity,
                  //                bifunction<u, ? super t, u> accumulator,
                  //                binaryoperator<u> combiner);
                  //类似上面, 但是第三个参数的combiner用于合并并发流中每个线程的result
                  // 单线程下是不执行的
                  integer sum3 = stream.of(1, 2, 3).reduce(1, integer::sum, (a,b) -> {
                      system.out.println(111);
                      return a + b;
                  });
                  system.out.println("sum : " + sum3); // sum : 7    
      • 收集
                // 收集,将流中的元素重新打包成集合
                list<string> collecttest = new arraylist<>(arrays.aslist("1,2,3", "4,5,6", "7,8,9"));
                // 主要有两种方法
                // 第一种,是自行实现收集操作,与 reduce()很相似
                // <r> r collect(supplier<r> supplier,
                //                  biconsumer<r, ? super t> accumulator,
                //                  biconsumer<r, r> combiner);
        
                // 一般主要使用第二种, 直接使用collectors类中已经实现的静态收集器
                //集合
                list<string> newlist = collecttest.stream().collect(collectors.tolist());//返回list
                set<string> newset = collecttest.stream().collect(collectors.toset());//返回set
                treeset<string> newtreeset = collecttest.stream().collect(collectors.tocollection(treeset::new));//自行定义返回的集合类型
        // 返回map,键值都为元素本身
                // 或者为元素的某个属性  比如: user::getid
                map<string, string> newmap = collecttest.stream().collect(collectors.tomap(function.identity(), function.identity()));
                // 拼接
                string join = collecttest.stream().collect(collectors.joining());
                string joinondelimiter = collecttest.stream().collect(collectors.joining(","));
                string joinallcondition = collecttest.stream().collect(collectors.joining(",", "begin", "end"));
                // 聚合
                string max = collecttest.stream().collect(collectors.collectingandthen(collectors.maxby(comparator.comparingint(string::length)), optional::get));
                optional<string> min = collecttest.stream().collect(collectors.minby(comparator.comparingint(string::length)));
                double average = collecttest.stream().collect(collectors.averagingint(s -> s.length()));
                integer sum = collecttest.stream().collect(collectors.summingint(s -> s.length()));
                // 映射,与map()近似,先处理映射,再使用收集器
                collecttest.stream().collect(collectors.mapping(s -> s.touppercase(), collectors.joining(",")));
                // 分组
                collecttest.stream().collect(collectors.groupingby(s -> s.length() % 2 == 0));
                collecttest.stream().collect(collectors.partitioningby(s -> s.length() % 2 == 0));
                // 缩减 reducing, 类似reduce()
                collecttest.stream().collect(collectors.reducing((s1, s2) -> string.valueof(integer.sum(s1.length(), s2.length()))));

         

  • parallelstream中的线程安全
    • 并行模式下不能保证线程的安全问题
    • 但,终端操作若使用collect()和reduce(),就能满足线程安全
  • 引用