Java 8 流式编程介绍

从迭代到流

流的概念

原先在处理集合里面的数据的时候,通常大家会采取这样的形式。比如对集合中的字符串长度大于10的进行个数的统计。我们通常会这么写

for (String s : list){
            if (s.length()>10){
                sum++;
            }
        }

但是如果用流式编程的话,我们可以这么写。

long sum = list.stream().filter(item -> item.length() > 10).count();

优点:
从代码的角度来看,流式编程更加的直接。不需写一个遍历去查找和过滤的操作。方法名字就能过直接告诉我们,我们意欲何为。

流的工作流程

1.创建一个流:有两种方式,一种是stream,一种是parallelStream。这两种方式都可以创建出来一个流。
2.流的操作:将初始流转换为其他流的中间操作,例如filter里面的过滤条件。
3.终止条件:流结束的条件。例如count();

流的特性

1.流不存储数据
2.流不会去修改源数据源
3.流得操作尽可能的是惰性的。也就是说,在你需要结果的时候流才会执行。举个例子:就是在count之前,你可以写多个的filter。

流的操作filter,map,flatMap

流的转换会产生一个新的流,它的元素派生于另一个流中的元素。

filter

filter会转换一个流,他的元素与某种条件相匹配。
在上面的代码中,我们能够看到filter中的引元(就是用一个字母代表一个世子)是peidicate<T>。即从T到boolean的函数。

Map

转换原先流里面的元素。

如果我们想按照某一种方式去转换源流里面的值的时候,我们可以用map方法传递执行该转换的函数。例如,把所有单词转成小些。

words.stream.map(String::toLoweCase);

map中紧跟的就是转换流元素的条件。

flatMap

map函数的使用是应用在流中每一个值的身上,其返回结果呢,是这个应用函数之后所产生的结果。但是有时候这个返回结果不是单纯的就是一个值,而是包含众多元素的流。这时候就使用flatMap函数

 

抽取子流和连接流

其实就是悬着前几个元素和跳过前几个元素。

stream.limit(n)

不用多说,顾名思义就是截取前n个元素转换为一个新的流。
比如下面:输出100个随机数
Stream.generate(Math::random).limit(100);

stream.skip(n)

顾名思义就是跳过前面n个元素,再把后面的元素转换为一个流。

stream.concat

这个作用是用于连接两个流,就是把第一个流里面的元素,和第二个流里面的元素合并起来。这里有一个要注意的就是,流里面的值的类型要注意,还有就是第一个流不能是无限流,不然第二个流不回呗处理的。就跟死循环是一个道理。

 

Optional类型对象

其主要解决的问题是空指针问题。

Optional<T>对象是一种包装器对象。一般来说在流约简的时候的返回对象(比如:max,min 之类的)。包装器对象要么包装了T对象,要么什么都没有。Optional<T>类型被当做一种更加安全的方式,用来替代类型T的引用,这种引用要么引用了某个对象。要么是null,但是它只有在正确使用的情况下线才会更加安全。

如何有效的使用呢:在值不存在的时候产生一个可替代物,只有值存在的时候才允许使用这个值。

 

说了这么多讲的还是一个概念,我们来讲讲实际的东西。
在代码里面如果一个对象,我们这么写的话,那么会有出现空指针的情况。

user.getUserName()

那么一般来说会在操作之前,先判断对象是否是null

if(user!=null){
    user.getUserName();
}

一个两个还行,数量一多if分支就多了起来,后期维护也很麻烦。
直接点,我们直接看Optional引入之后应该怎么写

Optional.ofNullable(user).orElse(user2);

这段代码的意思是,如果user为空,就返回备用方案user2,要是不为空那就返回原先的user
说到这里可能会觉得Optional只是给null的情况准备了一套备用方案而已。其实不然

我们再来说一下Optional的两个关键方法

ofNullable用来作为可能出现null值,和可选择值之间的桥梁。他会在值为null的时候返回一个空对象,不为bull的时候返回原先值

在举个例子
如果下面这一段代码,为了保证不出现null,会加了很多的判断。这样其实很不优雅

if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        Country country = address.getCountry();
        if (country != null) {
            String isocode = country.getIsocode();
            if (isocode != null) {
                isocode = isocode.toUpperCase();
            }
        }
    }
}

这样就行了

Optional.ofNullable(user)
.map(item1 -> item1.getAddress())
.map(item2 -> item2.getCountry())
.map(item3 -> item3.getIsocode())
.ofElse(XXXX);

看到这里是不是就明白了Optional出现的意义了呢。

这拜年顺便提一嘴

ofElser和ofElseGet的区别

区别在于:单判断的对象为空的时候,两个都会执行自己的备用方案。但是如果对象不为空的时候,ofElse还是会执行一遍自己的备用方案,但是ofElseGet不会。建议用ofElseGet

流收集结果

收集的话一般是用collect():将转换流收集到某个集合或者列表。
把这个拿出来说是因为这个collect有着很丰富的api。建议看一遍。这边就不一一列举。

流收集到映射表:toMap

作用就是可以理解为将list转换为Map。但是i这里说这个是因为又一点要注意。就是在转化过程中,要是出现多个key值相同的时候,如果是简单的写成下面这种情况的话,是会出现冲突的。

Collectors.toMap(User::getId,User::getName);

那么就要对出现冲突的现在做处理。比如,只取现存在的值。如下图(发现书本里面有,就不再写了)

 

 

并行流

故名思义,并行流的意思就是可以用来并行的处理流里面的数据。前面我们讲的都是顺序流。
在java7的时候,如果需要处理并行的处理一些数据的时候,可能我们需要把这些数据分为若干个,然后呢在分配不同的线程去执行。并且有时候需要其他条件的时候,还需要增加同步条件。

那么这时候呢有了并行流,它的作用就是把一个内容分成多个模块,并用不同的线程去处理它。

原理是什么呢?
并行流内部使用了默认的ForkJoinPool,它默认的线程数量就是你的处理器数量,这个值是由Runtime.getRuntime().avaliableProcessors()得到的
可以修改,但是不建议

工作原理:Fork/Join

就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务。拆到什么时候呢? 拆到不可再拆时,再将一个个的小任务运算的结果进行 join 汇总.

 

更加深层次原理请参考“工作窃取模式”。这边时间原因先不赘述。

以上内容来自于Java核心技术10版

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值