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
    
  • 相关阅读:
    leetcode--95. Unique Binary Search Trees II
    leetcode--96. Unique Binary Search Trees
    leetcode分类总结
    leetcode-115. Distinct Subsequences
    tcpdump安装配置及抓包分析
    dp经典案例
    tree的各种问题
    大数的阶乘算法
    由mmap引发的SIGBUS
    win10系统下如何查看端口被哪个进程占用
  • 原文地址:https://www.cnblogs.com/MrYuChen-Blog/p/14049242.html
Copyright © 2011-2022 走看看