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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM