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继承来的,调用注册关闭处理程序,关闭调用流(很少会被使用到)
-
继承关系:stream ——》 basestream<t, s extendsbasestream<t, s>> ——》autocloseable
-
流的操作
-
中间操作(intermediate operations)
- 一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),只有在指定的终端操作上才会执行中间操作。
-
无状态操作
- 处理流中的元素时,会对当前的元素进行单独处理。比如:谓词过滤操作,因为每个元素都是被单独进行处理的,所有它和流中的其它元素无关,因此被称为无状态操作。
-
有状态操作
- 某个元素的处理可能依赖于其他元素。比如查找最小值,最大值,和排序,因为他们都依赖于其他的元素。因此为称为有状态操作。
-
终端操作(terminal operations)
- 流只能有一个 terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
-
短路操作(short-circuiting)
- 对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的stream,但返回一个有限的新 stream。
- 对于一个 terminal 操作,如果它接受的是一个无限大的 stream,但能在有限的时间计算出结果。
-
中间操作(intermediate operations)
-
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()))));
-
缩减操作:把一个流缩减为一个值
-
1.创建stream:从数据源(如集合或数组)中获取一个流,或自行生成流
-
parallelstream中的线程安全
- 并行模式下不能保证线程的安全问题
- 但,终端操作若使用collect()和reduce(),就能满足线程安全
- 引用
上一篇: 深入浅析C++ traits技术
下一篇: 浅谈PHP7中的一些小技巧