从零开始的java学习Day17----------基础篇(Stream流)

Stream流

说到Stream便容易想到I/O Stream,而实际上,谁规定“流”就一定是“IO流”呢?在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。

Stream流的思想

  • Stream流,我们可以看成是一条生产线,我们要对多个元素完成多个目标时,可以把所有元素当作一个流模型,搭建一条流,流中可以制定对流模型执行一个步骤(筛选,映射,计数,跳过等等),完成步骤后,得到新的流模型,新的流模型执行下一步操作。
  • 这个我们制定的Stream流,也称为“函数模型”,每一步操作,就是将一个流模型转换为另一个流模型。

Stream流的特点

  • 1.Stream不会修改源数据。(执行的是新创建的流模型,不会对源数据进行修改)
  • 2.Stream流的部分操作具有延时性。(当只有流有终结方法的时候,流才会被执行)
  • 3.Stream不能存储元素
  • 4.Stream流是单向的

执行Stream流通常包括三个基本步骤:获取一个数据源→数据转换→执行操作获取想要的结果

获取流

获取流非常简单,我们这里只讲集合和数组获取流的方式

Collection获取流

Collection接口中有一个默认(default)方法stream来获取流,所以其所有实现类(所有单列集合)都可以通过这个方法来获取流对象
格式

单列集合对象名.stream()     //该方法会返回一个对应的流
Map集合获取流

Map集合不能直接获取流,只能通过keySet或者entrySet方法得到Map集合的键对象或者键值对对象的Set集合后,再调用他们的Stream来获取流
格式

Map集合对象名.keySet().Stream()          //返回一个map集合键的流
Map集合对象名.entrySet().Stream()        //返回一个map集合键值对的流
数组获取流

如果我们要获取数组的流,由于数组对象不可能添加默认方法,所以Stream接口中提供了静态方法of
注意:基本数据类型数组不能使用流
格式

stream.of(数组名)                        //返回一个该数组的对应流

常用方法

Stream流内的方法分为两种

  • 终结方法:返回值类型不再是Stream接口自身类型的方法,因此后续不在支持stream流的链式调用,本小节中,终结方法包括count和forEach方法。
  • 延迟方法:返回值类型仍然是Stream接口自身类型的方法,因此支持后续链式调用(除了终结方法外,其余方法均为延迟方法。)
filter(过滤 )

该方法可以将一个流转换成另一个子集流(延迟方法)
方法签名:

Stream<T> filter(Predicate<? super T> predicate);

该方法会接收一个predicate(判断接口)接口对象(可以是Lambda或方法引用)作为筛选条件,如果流内元素满足该接口的判断条件,返回的值是true,就会留到新的stream流中,否则就会舍弃。
格式

Stream流对象.filter(lambda表达式)          //每个元素依次执行lamdba表达式,满足的就留下来,形成一个新的流对象
count(统计个数)

count该方法是终结方法
正如旧集合Collection当中的size方法一样,流提供count方法来数一数其中的元素个数

方法签名:long count()

该方法会返回一个long值,代表流内元素的个数。

limit(取用前几个 )

limit方法可以对流进行截取,只取用前面n个,返回一个新的stream流(延迟方法)

方法签名:Stream<T> limit(long maxSize);          //返回一个只去前面maxSize个元素的新流

注意:如果截取长度超过流的元素个数,默认不执行

skip(跳过前几个 )

如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流(延迟方法)

方法签名:Stream<T> skip(long n);          //返回一个去掉前面n个元素的新流

注意:如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流

map(映射 )

如果需要将流中的元素映射到另一个流中,可以使用map方法(延迟方法)

方法签名:<R> Stream<R> map(Function<? super T, ? extends R> mapper);     //该方法会得到一个新的stream流,具体是什么取决于你写的lambda

该方法会通过给每个元素调用Function(转换接口)接口内的apply方法,每个元素执行后,得到一个新的元素,存入新的流

concat(组合 )

如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)    该流会将两个流合并
forEach(逐一处理 )
  • forEach该方法是终结方法
  • 虽然方法名字叫forEach,但是与for循环中的“for-each”昵称不同,该方法并不保证元素的逐一消费动作在流中是被有序执行的。
方法签名:void forEach(Consumer<? super T> action);             //该方法会消费掉流的元素,没有返回值

该方法接收一个Consumer(消费接口)接口函数,会将每一个流元素交给该函数进行处理

并发流

当需要对存在于集合或数组中的若干元素进行并发操作时,我们需要仔细考虑多线程环境下的原子性,竞争身子锁问题,而这对Stream流来说,很简单

转换为并发流

Stream的父接口java.util.stream.BaseStream中定义了一个parallel方法,我们直接使用Stream流对象调用一下即可变身成为支持并发操作的流,其返回值仍然为Stream类型
例:

单列集合对象.stream().parallel()          //该流已经支持并发操作

直接获取并发流

在通过集合获取流时,也可以直接调用parallelStream方法来直接获取支持并发操作的流

方法签名:default Stream<E> parallelStream()
单列集合对象.parallelStream()               //会得到一个支持并发的Stream流

使用并发的结果

流变为支持并发后,就会变得和多线程一样,多个流对象会进行抢夺Cpu操作
例如,多次执行下面这段代码,结果的顺序在很大概率上是不一样的

Stream.of(10, 20, 30, 40, 50) .parallel() .forEach(System.out::println);

收集Stream结果

对流操作完成之后,如果需要将其结果进行收集,例如获取对应的集合、数组等,我们可以使用下列几种方法

收集到集合中

Stream流提供collect方法,可以将流对象收集存储到集合中返回,该方法需要传入一个Collector接口对象来指定收集到哪种集合中,我们可以使用Collectors类的静态方法创建我们想要的接口对象
格式

流对象.collect(Collector对象)     //返回一个Collector指向的对应集合
Collectors.toList                //返回一个指向List集合的Collector对象
Collectors.toSet                 //返回一个指向Set集合的Collector对象

收集到数组中

Stream提供toArray方法来将结果放到一个数组中,由于泛型擦除的原因,返回值类型是Object[]的
格式

流对象.toArray               //返回一个Object[]类型的数组

有了Lambda和方法引用之后,可以使用toArray方法的另一种重载形式传递一个IntFunction<A[]>的函数,继而从外面指定泛型参数。

方法签名:<A> A[] toArray(IntFunction<A[]> generator);

有了它,我们得到的数组可以不用局限于Objcte
示例(收集一个String类型数组):

Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
String[] strArray = stream.toArray(String[]::new);   //引用想要类型的构造器,就能得到指定类型的数组
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值