zoukankan      html  css  js  c++  java
  • Stream(二)—数值流与对象流的转化及其方法使用

    Stream(二)—数值流与对象流的转化及其方法使用

    什么是数值流,什么是对象流?


    在上篇博客中,Stream初步认识中我们已经知道了什么是流。
    java8中将流又可以细分为:数值流和对象流,而平时我们用的最多的就是对象流。
    接下里我们就详细说说什么是数值流,什么又是对象流。


    直接上代码更容易理解:

        @Test
        public void test(){
    
            /*
             *1.1   数值流就是流中的元素都是基本数据类型(int),对象流就是流中元素为基本数据类型的包装数据类型(Integer)
             *      解析:
             *          * 数组的数据类型为包装类 :Integer  返回为包装类的流
             *          * 数组的数据类型为基本数据类型 :int  返回为流对象
             */
    
            //1.1.1   返回对象流
            Integer[]  arr = {1,2,3,4,5,6,7,8,9};
            Stream<Integer> stream = Arrays.stream(arr);
    
            //1.1.2   返数值流
            int[]  arrInts = {1,2,3,4,5,6,7,8,9};
            IntStream stream1 = Arrays.stream(arrInts);
    
        }
    

    从上面的测试示例上,很直观的就能明白,数值流就是:流中的元素都是基本数据类型,对象流就是流中的元素为基本数据类型的包装类

    公共集合


    下面所有测试用例中的集合,都是公共集合。

    
        List<Person> personList = new ArrayList<Person>() {
            {
                add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 2000, 18));
                add(new Person("Tamsen", "Brittany", "Java programmer", "female", 2371, 55));
                add(new Person("Floyd", "Donny", "Java programmer", "male", 3322, 25));
                add(new Person("Sindy", "Jonie", "Java programmer", "female", 35020, 15));
                add(new Person("Vere", "Hervey", "Java programmer", "male", 2272, 25));
                add(new Person("Maude", "Jaimie", "Java programmer", "female", 2057, 87));
                add(new Person("Shawn", "Randall", "Java programmer", "male", 3120, 99));
                add(new Person("Jayden", "Corrina", "Java programmer", "female", 345, 25));
                add(new Person("Palmer", "Dene", "Java programmer", "male", 3375, 14));
                add(new Person("Addison", "Pam", "Java programmer", "female", 3426, 20));
            }
        };
    

    为什么使用数值流


    在文章开头,我就提到我们平时使用最多的就是对象流,那为什么JAVA8还要提供数值流呢?
    首先抛出一个示例研究分析一下:

            //2.1.1    计算集合中的所有人的薪资总数
            int reduce = personList.stream()
              		// private int salary,age;map()方法通过方法的引用获取的每一个人的薪资都是int类型的
                                   .map(Person::getSalary)    
                                    .reduce(0, Integer::sum);
    

    这是一个简单的对集合流的操作:计算所有人薪资总和
    我们将这一段代码分为两部分进行分析:
    (~~~~)第一部分:map()取值操作,将所有对象的薪资取出
    (~~~~~~~~~~)首先是取值操作:

     @Data
    @Builder(toBuilder = true)
    @AllArgsConstructor
    @NoArgsConstructor
    public class Person {
    
        private String firstName, lastName, job, gender;
        private int salary,age;
    }
    通过插件获取的属性值是`int`类型的。
    

    (~~~~~~~~~~)我们在看map的源码:

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    

    根据map操作的源码我们可以看到,map操作后返回的是Integer类型的对象流Stream<R>
    那么这里就反应出一个问题:我们传入的是int基本数据类型,而map操作之后返回的却是Integer包装类型的对象流。也就是说这其中暗含了一步封箱操作

    (~~~~)第二部分:reduce()规约操作,通过引用sum方法进行求和
    (~~~~~~~~~~)首先我们sum求和方法的源码:

        public static int sum(int a, int b) {
            return a + b;
        }
    

    根据sum的源码我们发现Integer::sum 这个方法引用,它引用的方法的参数和返回值都是int。
    因此规约求和后的返回值确实是int。
    这就反应出第二个问题:map操作后,结果为Integer对象流Stream ,然后要进行reduce里的求和操作就需要先进行拆箱操作(integer->int),返回int类型。

    下面我们用数值流实现这个需求:

            //2.1.2 优化上面的操作(主要是减少封箱拆箱操作)
            int sum = personList.stream()
                                .mapToInt(Person::getSalary)   //  IntStream mapToInt(ToIntFunction<? super T> mapper);
                                .sum();    // int sum();
    

    然后将两者进行对比,使用数值流操作减少了中间的反复拆箱和封箱操作,而这些操作都是需要耗费内存资源的,同时对象流stream 也没有数值流这种简单直接的求和方法 sum()可调用(当然这只是其中一个方法)。这就是为什么java8 专门提供了数值流给我们使用。

    小结
    (~~~~~~~)* 使用对象流Stream操作的弊端,就是会频繁的进行封箱,拆箱,严重浪费了内存资源,
    (~~~~~~~)* 使用数值流可以减少这一行为,提高内存利用率。
    (~~~~~~~)* 另外操作对象流也没有数值流这样简单直接,也这是java8提供数值流的原因

    Numeric streams 数值流的使用


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

    1. 对象流转数值流


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

     //3.1   对象流转数值流    将对象流转换为数值流的常用方法是mapToInt、mapToDouble和mapToLong为例:
            Integer[]  arr = {1,2,3,4,5,6,7,8,9};
            Double[]  arrs = {1.1,2.5,3.6,4.4,5.5,6.4,7.1,8.1,9.7};
    
            //3.1.1     mapToInt()方法
            IntStream intStream = Arrays.stream(arr).mapToInt(e -> e.intValue());
    
            //3.1.2     mapToDouble()方法
            DoubleStream doubleStream = Arrays.stream(arrs).mapToDouble(e -> e.doubleValue());
    
            //3.1.3     mapToLong()方法
            LongStream longStream = Arrays.stream(arr).mapToLong(e -> e.longValue());
    

    2. 数值流转为对象流


    为什么:为数值流里面的方法受众面可能比较窄,方法拓展性也弱

            /*3.2   数值流转为对象流       boxed()方法
             *  为什么:为数值流里面的方法受众面可能比较窄,方法拓展性也弱
             */
            Integer[]  arrBoxed = {1,2,3,4,5,6,7,8,9};
            Stream<Integer> objStream = Arrays.stream(arrBoxed);//返回对象流
            IntStream intStream1 = objStream.mapToInt(e -> e.intValue());//对象流---->数值流
            Stream<Integer> boxed = intStream1.boxed();//数值流---->对象流
    
            //优化
            Arrays.stream(arrBoxed).mapToInt(e -> e.intValue()).boxed();
    

    3. 默认值OptionalInt


    Optional这个类,这是一个可以表示值存在或不存在的java容器。针对三种数值流,也分别有一个Optional数值类型版本:OptionalInt、OptionalDouble和OptionalLongOptional详解

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

     Integer reduce1 = Arrays.stream(arrNull).reduce(0, Integer::max);
     //打印结果为:0 (需要注意reduce的初始化参数为0)
    

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

    
            //3.3   默认值OptionalInt
            Integer[] arrNull = {};
            Stream<Integer> stream = Arrays.stream(arrNull);//对象流
            OptionalInt max1 = stream.mapToInt(i -> i.intValue()).max();//转为数值流求最大值
            int reduce = max1.orElse(10000000);//如果max方法返回值为空,则返回100000000
            System.out.println(reduce);
    
            int i1 = Arrays.stream(arrNull).mapToInt(i -> i.intValue()).max().orElse(10000000);
            System.out.printf("优化:"+i1);
    

    4. 数值范围


    Java8中还引入了两个可以用于IntStream和LongStream的静态方法帮助生成这种范围:range和rangeClosed

            //3.4   数值范围    Java 8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed
           
    		System.out.println("rangeClosed则包含结束值:");
    		//rangeClosed() 第一个参数接受起始值,第二个参数接受结束值
            IntStream.rangeClosed(1, 100).filter(s1 -> s1 % 2 == 0).forEach(e-> System.out.printf("%s%s"," ",e));
    
            System.out.println("range是不包含结束值的:");
    		//range()第一个参数接受起始值,第二个参数接受结束值
            IntStream.range(1, 100).filter(s1 -> s1 % 2 == 0).forEach(e-> System.out.printf("%s%s"," ",e));
    

    结束语


    辛苦搬砖的打工人,明天休息,面临着找地方搬家,太难了。
    继续保持搬砖精神,


    推荐博客:

    数值流与对象流的转化及其方法使用:https://blog.csdn.net/aigoV/article/details/102897762
    
  • 相关阅读:
    sql2slack alash3al 开源的又个轻量级工具
    pgspider fetchq 扩展docker镜像
    godns 集成coredns 的demo
    godns 简单dnsmasq 的dns 替换方案
    aviary.sh 一个基于bash的分布式配置管理工具
    使用coredns 的template plugin实现一个xip 服务
    nginx 代理 coredns dns 服务
    基于nginx proxy dns server
    几个不错的geodns server
    spring boot rest api 最好添加servlet.context-path
  • 原文地址:https://www.cnblogs.com/MrYuChen-Blog/p/14049242.html
Copyright © 2011-2022 走看看