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
posted @ 2020-11-27 17:18  Mr*宇晨  阅读(998)  评论(0编辑  收藏  举报