Java8新特性3:Stream3—数值流与对象流的转化及其方法使用

一、我们先用代码来感受一下什么叫数值流,什么 叫对象流:

上面例子:数值流就是流中的元素都是基本数据类型(int),对象流就是流中元素为基本数据类型的包装数据类型(Integer)

 二、为什么要用数值流?

    public static void main(String[] args) {

        List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH));


        int calories = menu.stream()
                .map(Dish::getCalories)
                .reduce(0, Integer::sum);

    }

上面这段代码作用求所有菜品卡路里的总和,现在我们拆分开来,分两部分看看这段代码:

1、第一部分解读

Dish::getCalories这个方法引用,引用的方法,它的参数,返回值都是基本数据类型 int(看看他的bean源码就知道):

menu.stream().map(Dish::getCalories) 返回是一个对象(Integer)流:

结论1:

上面这两段代码解读反映出一个问题:menu.stream().map(Dish::getCalories)  这段代码暗含了一个封箱操作(int->Integer),在这一步,代码把Stream的元素转为了Integer类型。

2、第二本分解读

再来看 Integer::sum 这个方法引用,它引用的方法的参数和返回值都是int,方法源码:

整段代码中的最后一步规约求和后的返回值确实是int:

结论2:

这里就反应出第二个问题,map操作后,结果为Integer对象流Stream<Integer> ,然后要进行reduce里的求和操作就需要先进行拆箱操作(integer->int)

3、现在让我们看看上面这段需求代码用数值流应该怎么实现:

上面代码可以在优化成这样:

好了,现在你应该知道使用对象流stream<Integer>操作的弊病了,那就是你可能需要频繁进行封箱、拆箱操作,而这些操作都是需要耗费内存资源的,同时对象流stream<Integer> 也没有数值流这种简单直接的求和方法 sum()可调用(当然这只是其中一个方法)。这就是为什么java8 专门提供了数值流给我们使用。

三、Numeric streams 数值流的使用

java8 引入了三个基本数据类型流接口:IntStream、DoubleStream和LongStream,分别将流中的元素特化为int、long和double,从而避免了暗含的装箱、拆箱成本。同时每个接口都带来了进行常用数值reduce操作的新方法,比如对数值流求和的sum(如果流是空的,sum返回值为0),还有max、min、average等。

1、对象流转数值流:

将对象流转换为数值流的常用方法是mapToInt、mapToDouble和mapToLong,以mapToInt为例:

2、数值流转为对象流

我们有时也需要将数值流转为对象流,因为数值流里面的方法受众面可能比较窄,方法拓展性也弱。比如 IntStream上的操作只能产生原始整数: IntStream 的map 操作接受的Lambda 表达式必须接受int 并返回int 。但是你此时可能想要生成另一类值或者你想传入一个其他类型的值到一个Lambda 表达式,比如Dish对象。这时你就需要访问Stream接口中定义的那些更广义的操作。数值流转为对象流方法是 boxed():

3、默认值OptionalInt

前面博客简单说了一下Optional这个类,这是一个可以表示值存在或不存在的java容器。针对三种数值流,也分别有一个Optional数值类型版本:OptionalInt、OptionalDouble和OptionalLong。

这个的作用体现在哪里呢?看下面这个例子,它的流是空的,但是我们对这个流进行reduce操作时,我们给了它一个初始参数 0,并希望进一步求得到这个流中的最大值,运行程序结果为:0。那么我们这个流中的最大值就是0了吗?这个结果是我们想要的吗?显然不是,事实是这个流不存在最大值,因为他是空的,输出的0不过是我们reduce的一个初始参数0。

 好了这个时候 OptionalInt、OptionalDouble和OptionalLong的作用就出来了:

当你需要考虑到流没有最大值的情况时(当然这只是一种需求情况而已),你就可以显式处理OptionalInt,去定义一个默认值,避免出现上面那个例子的混淆结果:

        Integer[] arr = {};
        Stream<Integer> stream = Arrays.stream(arr);//对象流
        OptionalInt max1 = stream.mapToInt(i -> i.intValue()).max();
        int reduce = max1.orElse(10000000);//如果max方法返回值为空,则返回100000000
        System.out.println(reduce);

4、数值范围

Java 8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed。这两个方法都接受两个参数:第一个参数接受起始值,第二个参数接受结束值。但range是不包含结束值的,而rangeClosed则包含结束值:

        long count = IntStream.rangeClosed(1, 100).filter(s1 -> s1 % 2 == 0).count();//偶数个数:50
        long count1 = IntStream.range(1, 100).filter(s1 -> s1 % 2 == 0).count();//偶数个数:49

5、数值流综合应用案例:

从整数 1-50 里面创建一个勾股数流(勾股数都为整数),并输出前3组勾股数组,打印出来格式 like this:
3, 4, 5
5, 12, 13
6, 8, 10

        //给出值域范围
        Stream<double[]> rStream2 = IntStream.rangeClosed(1, 50)
                //转为对象流
                .boxed()
                //flatMap 把下面生成的一个个单独三元数组流,扁平化为一个三元数组流
                .flatMap(q ->   
                        IntStream.rangeClosed(q, 50)
                                //mapToObj 求出所有满足q*q+w*w+(q*q+w*w)的三元数组 组成的一个个流
                                .mapToObj(w -> new double[]{q, w, Math.sqrt(q * q + w * w)}) 
                                //q,w是从整数(1-50)里取的,肯定为整数,所以只要过滤掉三元数组中第三个元素不为整数的即可
                                .filter(d -> d[2] % 1 == 0) 
                );
        //打印前三组
        rStream2.limit(3).forEach(t->System.out.println(t[0]+"-"+t[1]+"-"+t[2]));

java8 新特性解析(更新中): 

Java8新特性1:lambda表达式入门--由浅入深,从单发步枪迈向自动步枪 

Java8新特性2:方法引用--深入理解双冒号::的使用

Java8新特性3:Stream1——什么是Stream,Stream的特性,如何使用Stream,Stream与Collection集合的区别

Java8新特性3:Stream2—一文详解Stream API,让你快速理解Stream Api提供的诸多常用方法

Java8新特性3:Stream3—数值流与对象流的转化及其方法使用​​​​​​​

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值