JAVA8 Stream流之reduce()方法詳解


JAVA8 Stream流之reduce()方法詳解

reduce()簡介


  • Reduce 原意:減少,縮小
  • 根據指定的計算模型將Stream中的值計算得到一個最終結果
    解釋reduce 操作可以實現從Stream中生成一個值,其生成的值不是隨意的,而是根據指定的計算模型。比如,之前提到count、min和max方法,因為常用而被納入標准庫中。事實上,這些方法都是reduce操作。

reduce三個override的方法


reduce方法有三個override的方法:

Optional reduce(BinaryOperator accumulator);

T reduce(T identity, BinaryOperator accumulator);

<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

公共集合


測試代碼中的所有集合,都是該集合。

    List<Person> javaProgrammers = 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));
        }
    };

方式一reduce(BinaryOperator accumulator)


  • Optional<T> reduce(BinaryOperator<T> accumulator);
    我們先看第一個變形,參數列表為一個函數接口BinaryOperator<T>,
    BinaryOperator源碼:
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
      public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }
    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}

看BinaryOperator接口源碼,我們可以看到,它又繼承了 BiFunction<T,T,T>.
另外,在BinaryOperator接口中又定義了另個靜態方法為minBy和maxBy,
上面我們提到BinaryOperator接口繼承了BiFunction<T,T,T>,我們看一下BiFunction<T,T,T>源碼:

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);//接收兩個參數 t 和 u, 返回 R
}

Bifunction中有一個apply方法,接收兩個參數,返回一個結果
小結: 不管是BinaryOperator類還是最終繼承的BiFunction類,在類上都有@FunctionalInterface注解,因此reduce(BinaryOperator<T> accumulator)方法需要一個函數式接口參數,該函數式接口需要兩個參數,返回一個結果(reduce中返回的結果會作為下次累加器計算的第一個參數),也就是累加器,最終得到一個Optional對象

測試示例代碼:

    @Test
    public void Test() {
        int asInt = javaProgrammers.stream()
                                    .mapToInt(Person::getSalary)//返回數值流,減少拆箱封箱操作,避免占用內存  IntStream
                                    .reduce((x, y) -> x += y)// int
                                    .getAsInt(); //return int
        System.out.printf("方式一   reduce(BinaryOperator<T> accumulator)   求薪資測試結果:"+asInt);

        /*解析:
             1. reduce(BinaryOperator<T> accumulator)    reduce方法接受一個函數,這個函數有兩個參數
             2. 第一個參數是上次函數執行的返回值(也稱為中間結果),第二個參數是stream中的元素,這個函數把這兩個值相加,得到的和會被賦值給下次執行這個函數的第一個參數
         *注意:
             1.第一次執行的時候第一個參數的值是Stream的第一個元素,第二個參數是Stream的第二個元素
             2.方法返回值類型是Optional
         */
    }

方式二reduce(T identity, BinaryOperator accumulator)


  • T reduce(T identity, BinaryOperator<T> accumulator);
    與第一種變形相同的是都會接受一個BinaryOperator函數接口,不同的是其會接受一個identity參數,identity參數與Stream中數據同類型,相當於一個的初始值,通過累加器accumulator迭代計算Stream中的數據,得到一個跟Stream中數據相同類型的最終結果。
    測試示例代碼:
    @Test
    public void test1(){
        int reduce = javaProgrammers.stream().mapToInt(Person::getSalary).reduce(10000, (x, y) -> x += y);
        System.out.printf("方式二  reduce(T identity, BinaryOperator<T> accumulator)   求薪資測試結果:"+reduce);

        /*注意:
         *      1.與方式一相比設置了累加器的初始值,參數一(x)則不再是Stream中的第一個數據而是設置的初始值(10000)其他相同
         */
    }

打印結果:

方式一   reduce(BinaryOperator<T> accumulator)   求薪資測試結果:57308
方式二  reduce(T identity, BinaryOperator<T> accumulator) 求薪資測試結果:67308 //初始值10000

方式三 reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner)

  • \<U\> U reduce(U identity,BiFunction\<U, ? super T, U\> accumulator,BinaryOperator\<U\> combiner);
    我們先觀察分析再次被改變的參數列表:

\(~~~~~~~\) 1. 第一個參數:返回實例u,傳遞你要返回的U類型對象的初始化實例u

\(~~~~~~~\) 2. 第二個參數:累加器accumulator,可以使用lambda表達式,聲明你在u上累加你的數據來源t的邏輯,例如(u,t)->u.sum(t),此時lambda表達式的行參列表是返回實例u和遍歷的集合元素t,函數體是在u上累加t

\(~~~~~~~\) 3. 第三個參數:參數組合器combiner,接受lambda表達式。

根據參數我們一步一步分析代碼示例:

    @Test
    public void test2() {
        ArrayList<Integer> accResult_ = Stream.of(1, 2, 3, 4)
                //第一個參數,初始值為ArrayList
                .reduce(new ArrayList<Integer>(),
                        //第二個參數,實現了BiFunction函數式接口中apply方法,並且打印BiFunction
                        new BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>>() {
                            @Override
                            public ArrayList<Integer> apply(ArrayList<Integer> acc, Integer item) {

                                acc.add(item);
                                System.out.println("item: " + item);
                                System.out.println("acc+ : " + acc);
                                System.out.println("BiFunction");
                                return acc;
                            }
                            //第三個參數---參數的數據類型必須為返回數據類型,改參數主要用於合並多個線程的result值
                            // (Stream是支持並發操作的,為了避免競爭,對於reduce線程都會有獨立的result)
                        }, new BinaryOperator<ArrayList<Integer>>() {
                            @Override
                            public ArrayList<Integer> apply(ArrayList<Integer> acc, ArrayList<Integer> item) {
                                System.out.println("BinaryOperator");
                                acc.addAll(item);
                                System.out.println("item: " + item);
                                System.out.println("acc+ : " + acc);
                                System.out.println("--------");
                                return acc;
                            }
                        });
        System.out.println("accResult_: " + accResult_);

        System.out.println("------------------lambda優化代碼-----------------");

        ArrayList<Integer> newList = new ArrayList<>();

        ArrayList<Integer> accResult_s = Stream.of(1,2,3,4)
                .reduce(newList,
                        (acc, item) -> {
                            acc.add(item);
                            System.out.println("item: " + item);
                            System.out.println("acc+ : " + acc);
                            System.out.println("BiFunction");
                            return acc;
                        }, (acc, item) -> null);
        System.out.println("accResult_s: " + accResult_s);
    }

\(~~~~\)示例代碼中,第一個參數是ArrayList,在第二個函數參數中打印了“BiFunction”,而在第三個參數接口中打印了函數接口中打印了”BinaryOperator“.看下面的打印結果,只打印了“BiFunction”,而沒有打印”BinaryOperator“,也就是說第三個函數參數並沒有執行。分析參數時我們知道了該變形可以返回任意類型的數據
\(~~~~\)對於第三個函數參數,為什么沒有執行,而且其參數必須為返回的數據類型?這是因為Stream是支持並發操作的,為了避免競爭,對於reduce線程都會有獨立的result,combiner的作用在於合並每個線程的result得到最終結果。這也說明了了第三個函數參數的數據類型必須為返回數據類型了。 java8 reduce方法中的第三個參數combiner有什么作用?
打印結果:

item: 1
acc+ : [1]
BiFunction
item: 2
acc+ : [1, 2]
BiFunction
item: 3
acc+ : [1, 2, 3]
BiFunction
item: 4
acc+ : [1, 2, 3, 4]
BiFunction

另外需要注意:因為第三個參數用來處理並發操作,如何處理數據的重復性,應多做考慮,否則會出現重復數據!

結束語


路漫漫其修遠行,吾將上下而求索


推薦參考博客:

https://blog.csdn.net/IO_Field/article/details/54971679
https://blog.csdn.net/weixin_43860260/article/details/94875064


免責聲明!

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



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