JDK Stream流使用介紹

       Stream 是對集合(Collection)對象功能的增強,它專注於對集合對象進行各種非常便利、高效的聚合操作,或者大批量數據操作。通常我們需要多行代碼才能完成的操作,藉助於Stream流式處理可以很簡單的實現。

       Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的Iterator。同時Stream提供串行和並行兩種模式進行匯聚操作。比如你的Stream裏面有很多數據,Stream可以開多個線程每個線程處理一部分。最後把結果彙總起來。

       在開始之前我們先用一個圖來整體的概況下Stream。如下所示:

Stream

一 Stream流創建

       想使用Stream流,首先咱得先創建一個Stream流對象。創建Steam需要數據源.這些數據源可以是集合、可以是數組、可以使文件、甚至是你可以去自定義等等。

1.1 集合作爲Stream數據源

       集合Collection作爲Stream的數據源,應該也是我們用的最多的一種數據源了。Collection裏面也提供了一些方法幫助我們把集合Collection轉換成Stream。

1.1.1 stream()方法

       調用Collection.stream()函數創建一個Stream對象。相當於把集合Collection裏面的數據都導入到了Stream裏面去了。

    @Test
    public void collectionStream() {

        List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
        // 使用List創建一個流對象
        Stream<Integer> stream = list.stream();
        // TODO: 對流對象做處理
    }

1.1.2 parallelStream()方法

       調用Collection.parallelStream()創建Stream對象。

    @Test
    public void collectionParallelStream() {

        List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
        // 使用List創建一個流對象
        Stream<Integer> stream = list.parallelStream();
        // TODO: 對流對象做處理
    }

parallelStream()與stream()區別是parallelStream()使用多線程併發處理最後子啊彙總結果,而stream()是單線程。所以相對來說parallelStream()效率要稍微高點。

1.2 數組作爲Stream數據源

       數組也可以作爲Stream的數據源。我們可以通過Arrays.stream()方法把一個數組轉化成流對象。Arrays.stream()方法很豐富,有很多個。大家可以根據實際情況使用。

    @Test
    public void arrayStream() {

        int[] intArray = new int[10];
        for (int index = 0; index < intArray.length; index++) {
            intArray[index] = index;
        }
        // 使用數組創建一個流對象
        IntStream stream = Arrays.stream(intArray);
        // TODO: 對流對象做處理
    }

1.3 BufferedReader作爲Stream數據源

       我們也可以把BufferedReader裏面lines方法把BufferedReader裏面每一行的數據作爲數據源生成一個Stream對象。

    @Test
    public void bufferedReaderStream() {

        File file = new File("/home/tuacy/github/google-guava-study/src/main/resources/application.yml");
        try {
            // 把文件裏面的內容一行一行的讀出來
            BufferedReader in = new BufferedReader(new FileReader(file));
            // 生成一個Stream對象
            Stream<String> stream = in.lines();
            // TODO: 對流對象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4 File作爲Stream數據源

       Files裏面多個生成Stream對象的方法,都是對Path(文件)的操作。有的是指定Path目錄下所有的子文件(所有的子文件相當於是一個列表了)作爲Stream數據源,有的把指定Path文件裏面的每一行數據作爲Stream的數據源。

1.4.1 Files.list()

       列出指定Path下面的所有文件。把這些文件作爲Stream數據源。

    @Test
    public void fileListStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
        try {
            // 找到指定path下的所有的文件
            Stream<Path> stream = Files.list(path);
            // TODO: 對流對象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4.2 Files.walk()

       Files.walk()方法用於遍歷子文件(包括文件夾)。參數maxDepth用於指定遍歷的深度。把子文件(子文件夾)作爲Stream數據源。

    @Test
    public void fileWalkStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
        try {
            // 第二個參數用於指定遍歷幾層
            Stream<Path> stream = Files.walk(path, 2);
            // TODO: 對流對象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4.3 Files.find()

       Files.find方法用於遍歷查找(過濾)子文件。參數裏面會指定查詢(過濾)條件。把過濾出來的子文件作爲Stream的數據源。

    @Test
    public void fileFindStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
        try {
            // 找到指定path下的所有不是目錄的文件
           Stream<Path> stream = Files.find(path, 2, (path1, basicFileAttributes) -> {
               // 過濾掉目錄文件
               return !basicFileAttributes.isDirectory();
           });
            // TODO: 對流對象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4.4 Files.lines()

       Files.lines方法是把指定Path文件裏面的每一行內容作爲Stream的數據源。

    @Test
    public void fileLineStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources\\application.yml");
        try {
            // 生成一個Stream對象
            Stream<String> stream = Files.lines(path);
            // TODO: 對流對象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.5 自己構建Stream

       我們也可以自己去創建Stream自己提供數據源。Stream類裏面提供of()、iterate()、generate()、builder()等一些方法來創建Stream,Stream的數據源我們自己提供。

1.5.1 Stream.of()

       Stream.of()函數參數就是數據源。

       Stream<Integer> ofSteam = Stream.of(1,2,3,4,5,6);

1.5.2 Stream.iterate()

       Stream.iterate()可以用來生成無限流,函數需要兩個參數:第一個參數是初始值、第二個參數用於確定怎麼根據前一個元素的值生成下一個元素。

        // Stream.iterate() 流式迭代器 Stream.iterate()函數的第二個參數告訴你怎麼去生成下一個元素
        Stream<BigInteger> integers = Stream.iterate(
                BigInteger.ONE,
                new UnaryOperator<BigInteger>() {

                    @Override
                    public BigInteger apply(BigInteger bigInteger) {
                        return bigInteger.add(BigInteger.ONE);
                    }
                });
        // 簡單輸出
        integers.limit(10).forEach(new Consumer<BigInteger>() {
            @Override
            public void accept(BigInteger bigInteger) {
                System.out.println(bigInteger.intValue());
            }
        });

1.5.3 Stream.generate()

       Stream.generate()也是用於生成一個無限流。參數用於獲取每個元素。

        // Stream.generate() 生成無限流
        Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
            @Override
            public Double get() {
                return java.lang.Math.random() * 100;
            }
        });
        // 簡單輸出前10個值
        generateA.limit(10).forEach(new Consumer<Double>() {
            @Override
            public void accept(Double bigInteger) {
                System.out.println(bigInteger.intValue());
            }
        });

1.5.4 Stream.build()

       Stream.build()通過建造者模式生成一個Stream建造器。然後把需要加入Stream裏面的數據源一個一個通過建造器添加進去。

        // Stream.builder()構造一個Stream對象
        Stream.Builder<Integer> build = Stream.<Integer>builder().add(1)
                .add(2)
                .add(3);
        build.accept(4);
        build.accept(5);
        build.build().forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
        // TODO: 對流對象做處理

1.6 其他Stream創建方式

       Stream其他創建方式我們就不一一舉例了。有如下方式。

  • Random.ints()。
  • BitSet.stream()。
  • Pattern.splitAsStream(java.lang.CharSequence)
  • JarFile.stream()。

二 Stream流操作(操作符)

       Stream流操作就是對Stream流的各種處理。Stream裏面已經給提供了很多中間操作(我們一般稱之爲操作符)。

Stream提供的流操作符。

Stream流操作符 解釋
filter 過濾
map 對流裏面每個元素做轉換
mapToInt 把流裏面的每個元素轉換成int
mapToLong 流裏面每個元素轉換成long
mapToDouble 流裏面每個元素轉換成double
flatMap 流裏面每個元素轉換成Steam對象,最後平鋪成一個Stream對象
flatMapToInt 流裏面每個元素轉換成IntStream對象,最後平鋪成一個IntStream對象
flatMapToLong 流裏面每個元素轉換成LongStream對象,最後平鋪成一個LongStream對象
flatMapToDouble 流裏面每個元素轉換成DoubleStream對象,最後平鋪成一個DoubleStream對象
distinct 去重
sorted 排序
peek 查看流裏面的每個元素
limit 返回前n個數
skip 跳過前n個元素

       Stream提供了這麼多的操作符,而且這些操作符是可以組合起來使用。關於每個操作符的使用我們用一個簡單的實例代碼來說明。

2.1 filter

       filter用於對流裏面的數據做過濾操作。

    // 過濾
    @Test
    public void filter() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 過濾出偶數
        Stream<Integer> filterStream = stream.filter(new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer % 2 == 0;
            }
        });
        // 簡單輸出
        filterStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.2 map

       map用於對流裏面的元素做轉換。

    // 轉換
    @Test
    public void map() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 整數轉換爲String
        Stream<String> mapStream = stream.map(new Function<Integer, String>(){
            @Override
            public String apply(Integer integer) {
                return String.valueOf(integer);
            }
        });
        // 簡單輸出
        mapStream.forEach(new Consumer<String>() {
            @Override
            public void accept(String integer) {
                System.out.println(integer);
            }
        });
    }

2.3 mapToInt、mapToLong、mapToDouble

       mapToInt、mapToLong、mapToDouble用於將流裏面的元素轉換成對應的類型。

    // 轉換
    @Test
    public void mapToInt() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 整數轉換爲String
        IntStream mapStream = stream.mapToInt(new ToIntFunction<Integer>(){

            @Override
            public int applyAsInt(Integer value) {
                return value == null ? 0 : value;
            }
        });
        // 簡單輸出總和
        System.out.println(mapStream.sum());
    }

2.4 flatMap、flatMapToInt、flatMapToLong、flatMapToDouble

       flatMap、flatMapToInt、flatMapToLong、flatMapToDouble也是對每個元素的轉換,不過他們和map的不同點在於,他們是吧每個元素轉換成一個Stream流,最終在平鋪成一個Stream流。

    // 轉換
    @Test
    public void flatMap() {
        Stream<String> stream = Stream.of("java:1", "android:2", "ios:3");
        // 整數轉換爲String
        Stream<String> rerStream = stream.flatMap(
                new Function<String, Stream<String>>() {
                    @Override
                    public Stream<String> apply(String s) {
                        // 分割
                        Iterable<String> iterableList = Splitter.on(':').trimResults() // 移除前面和後面的空白
                                .omitEmptyStrings()
                                .split(s);
                        return Lists.newArrayList(iterableList).parallelStream();
                    }
                });
        // 簡單輸出
        rerStream.forEach(new Consumer<String>() {
            @Override
            public void accept(String integer) {
                System.out.println(integer);
            }
        });
    }
    // 轉換
    @Test
    public void reduce() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 所有的元素相加,在加上20
        Integer reduceValue = stream.reduce(20, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                System.out.println(integer);
                System.out.println(integer2);
                return integer + integer2;
            }
        });
        System.out.println(reduceValue);
    }

2.5 distinct

       distinct操作符用於去重。

    // 去重
    @Test
    public void distinct() {
        Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
        Stream<Integer> rerStream = stream.distinct();
        // 簡單輸出
        rerStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.6 sorted

       sorted操作符用於對流裏面的元素排序。

    // 排序
    @Test
    public void sorted() {
        Stream<Integer> stream = Stream.of(1,2,3,2,5,4,8,6);
        Stream<Integer> rerStream = stream.sorted(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                if (o1.equals(o2)) {
                    return 0;
                } else {
                    return o1 > o2 ? 1 : -1;
                }
            }
        });
        // 簡單輸出
        rerStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.7 peek

       peek操作符用於查看流裏面的每個元素。在多個操作符同時使用的時候的中間加入peek操作符可以參考每個操作符之後的結果。

    // 查看
    @Test
    public void peek() {
        Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
        // 查看
        Stream<Integer> reStream = stream.peek(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
        // 簡單輸出
        reStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.8 limit

       limit操作符用於取流前面多少個元素。

    // limit
    @Test
    public void limit() {
        Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
        // limit
        Stream<Integer> reStream = stream.limit(3);
        // 簡單輸出
        reStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.9 skip

       skip用於跳過裏面的多少個元素。

    // skip
    @Test
    public void skip() {
        Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
        // skip
        Stream<Integer> reStream = stream.skip(3);
        // 簡單輸出
        reStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

三. Stream流終端操作

       Stream流終端操作是流式處理的最後一步,之前已經對Stream做了一系列的處理之後。該拿出結果了。我們可以在終端操作中實現對流的遍歷、查找、歸約、收集等等一系列的操作。

Stream流終端操作提供的函數有

終端操作符 解釋
forEach 遍歷
forEachOrdered 如果流裏面的元素是有順序的則按順序遍歷
toArray 轉換成數組
reduce 歸約 - 根據一定的規則將Stream中的元素進行計算後返回一個唯一的值
collect 收集 - 對處理結果的封裝
min 最小值
max 最大值
count 元素的個數
anyMatch 任何一個匹配到了就返回true
allMatch 所有都匹配上了就返回true
noneMatch 沒有一個匹配上就返回true
findFirst 返回滿足條件的第一個元素
findAny 返回某個元素

       關於Stream終端操作部分,我們就着重講下collect()函數的使用。因爲其他的終端操作符都很好理解。collect()稍稍複雜一點。

3.1 collect()

       collect()的使用主要在於對參數的理解,所有我們這裏要專門講下collect()函數的參數Collector這個類,以及怎麼去構建Collector對象。只有在瞭解了這些之後,咱們纔可以熟練的把他們用在各種場景中。

3.1.1 Collector

       Collector類目前沒別的用處,就是專門用來作爲Stream的collect()方法的參數的。把Stream裏面的數據轉換成我們最終想要的結果上。

Collector各個方法,以及每個泛型的介紹,都在下面詳細的解釋了。如果有疑問可以留言。

/**
 * Collector是專門用來作爲Stream的collect方法的參數的
 *
 * 泛型含義
 * T:是流中要收集的對象的泛型
 * A:是累加器的類型,累加器是在收集過程中用於累積部分結果的對象。
 * R:是收集操作得到的對象(通常但不一定是集合)的類型。
 */
public interface Collector<T, A, R> {
    /**
     * 生成結果容器,容器類型爲A
     * (多線程的情況下可能會調用多次,開多個線程同時去處理一個流,每個線程調用一次)
     */
    Supplier<A> supplier();

    /**
     * A對應supplier()函數創建的結果容器
     * T對應Stream流裏面一個一個的元素
     * 用於消費元素,也就是歸納元素,一般在這個裏面把流裏面的元素T(也可以轉換下)放到supplier()創建的結果T裏面去
     */
    BiConsumer<A, T> accumulator();

    /**
     * 用於兩個兩個合併並行執行的線程的執行結果,將其合併爲一個最終結果A
     * 多線程的情況下,多個線程並行執行。每個線程產生一個結果
     */
    BinaryOperator<A> combiner();

    /**
     * 用於將之前整合完的結果A轉換成爲R
     *
     * combiner()完成之後了A, 這裏還可以在轉一道。生成你自己想要的結果
     */
    Function<A, R> finisher();

    /**
     * characteristics表示當前Collector的特徵值,
     * 這是個不可變Set
     * 它定義了收集器的行爲--尤其是關於流是否可以多線程並行執行,以及可以使用哪些優化的提示
     */
    Set<Characteristics> characteristics();

    /**
     * 它定義了收集器的行爲--尤其是關於流是否可以並行歸約,以及可以使用哪些優化的提示
     */
    enum Characteristics {
        /**
         * accumulator函數可以從多個線程同時調用,且該收集器可以並行歸約流。如果收集器沒有標爲UNORDERED,
         * 那它僅在用於無序數據源時纔可以並行歸約
         * 多線程並行
         */
        CONCURRENT,

        /**
         * 歸約結果不受流中項目的遍歷和累積順序的影響(無序)
         */
        UNORDERED,

        /**
         * 無需轉換結果
         */
        IDENTITY_FINISH
    }


    /**
     * 四參方法,用於生成一個Collector,T代表流中的一個一個元素,R代表最終的結果
     */
    public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
                                              BiConsumer<R, T> accumulator,
                                              BinaryOperator<R> combiner,
                                              Characteristics... characteristics);

    /**
     * 五參方法,用於生成一個Collector,T代表流中的一個一個元素,A代表中間結果,R代表最終結果,finisher用於將A轉換爲R
     */
    public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
                                                 BiConsumer<A, T> accumulator,
                                                 BinaryOperator<A> combiner,
                                                 Function<A, R> finisher,
                                                 Characteristics... characteristics);



}

       有了上面的介紹,接下來我們自己來new一個Collector對象,把我們Steam流裏面的數據轉換成List。(當然了Collectors類裏面有提供這個方法,這裏我們自己寫一個也是爲了方便大家的理解)

    // 自己來組裝Collector,返回一個List
    @Test
    public void collectNew() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> intList = stream.collect(
                new Collector<Integer, List<Integer>, List<Integer>>() {
                    // 生成結果容器,容器類型爲,我們這裏爲List<Integer>
                    @Override
                    public Supplier<List<Integer>> supplier() {
                        return new Supplier<List<Integer>>() {

                            @Override
                            public List<Integer> get() {
                                return new ArrayList<>();
                            }
                        };
                    }

                    // 把流裏面的結果都放到結果容器裏面去
                    @Override
                    public BiConsumer<List<Integer>, Integer> accumulator() {
                        return new BiConsumer<List<Integer>, Integer>() {
                            @Override
                            public void accept(List<Integer> integers, Integer integer) {
                                integers.add(integer);
                            }
                        };
                    }

                    // 兩個兩個合併並行執行的線程的執行結果,將其合併爲一個最終結果A
                    @Override
                    public BinaryOperator<List<Integer>> combiner() {
                        return new BinaryOperator<List<Integer>>() {
                            @Override
                            public List<Integer> apply(List<Integer> left, List<Integer> right) {
                                left.addAll(right);
                                return left;
                            }
                        };
                    }

                    // 可以對最終的結果做一個轉換操作
                    @Override
                    public Function<List<Integer>, List<Integer>> finisher() {
                        return new Function<List<Integer>, List<Integer>>() {
                            @Override
                            public List<Integer> apply(List<Integer> integers) {
                                return integers;
                            }
                        };
                    }

                    // 特徵值
                    @Override
                    public Set<Characteristics> characteristics() {
                        return EnumSet.of(Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH);
                    }
                });

        for (Integer item : intList) {
            System.out.println(item);
        }
    }

3.1.2 Collectors

       Collectors是Collector的工具類,用來創建Collector對象。它內部以及給我們提供了各種各樣的創建Collector對象的方法,已經能滿足我們大部分的需求了,我們可以直接拿來使用,非常方便。

Collectors各個方法介紹

public final class Collectors {


    /**
     * 將流中的元素全部放置到一個集合中返回
     *
     * collectionFactory參數用於創建Collection對象(比如List,LinkeList等等)
     */
    public static <T, C extends Collection<T>>
    Collector<T, ?, C> toCollection(Supplier<C> collectionFactory);

    /**
     * 將流中的元素放置到一個列表集合中去。這個列表默認爲ArrayList
     */
    public static <T>
    Collector<T, ?, List<T>> toList();

    /**
     * 將流中的元素放置到一個無序集set中去。默認爲HashSet
     */
    public static <T>
    Collector<T, ?, Set<T>> toSet();

    /**
     * joining的目的是將流中的元素全部以字符序列的方式連接到一起,可以指定連接符,甚至是結果的前後綴
     */
    public static Collector<CharSequence, ?, String> joining();
    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter);
    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix);

    /**
     * 這個映射是首先對流中的每個元素進行映射,即類型轉換,然後再將新元素以給定的Collector進行歸納
     */
    public static <T, U, A, R>
    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                               Collector<? super U, A, R> downstream);

    /**
     * 該方法是在歸納動作結束之後,對歸納的結果進行再處理
     */
    public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
                                                                Function<R,RR> finisher);

    /**
     * 該方法用於計數
     */
    public static <T> Collector<T, ?, Long> counting();

    /**
     * 生成一個用於獲取最小/最大值的Optional結果的Collector
     */
    public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator);
    public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator);

    /**
     * 生成一個用於求元素和的Collector,首先通過給定的mapper將元素轉換類型,然後再求和
     */
    public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper);
    public static <T> Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper);
    public static <T> Collector<T, ?, Double> summingDouble(ToDoubleFunction<? super T> mapper);

    /**
     * 生成一個用於求元素平均值的Collector,首選通過參數將元素轉換爲指定的類型
     */
    public static <T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper);
    public static <T> Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> mapper);
    public static <T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper);

    /**
     * 規約 對流中的元素做統計歸納作用
     * 和Stream類裏面的reducing操作符一樣
     */
    public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op);
    public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op);
    public static <T, U> Collector<T, ?, U> reducing(U identity,
                                Function<? super T, ? extends U> mapper,
                                BinaryOperator<U> op);

    /**
     * 這個方法是用於生成一個擁有分組功能的Collector
     */
    public static <T, K> Collector<T, ?, Map<K, List<T>>>
    groupingBy(Function<? super T, ? extends K> classifier);
    public static <T, K, A, D>
    Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
                                          Collector<? super T, A, D> downstream);
    public static <T, K, D, A, M extends Map<K, D>>
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream);

    /**
     * 和groupingBy方法一樣,只是返回的Collector是並行的
     */
    public static <T, K>
    Collector<T, ?, ConcurrentMap<K, List<T>>>
    groupingByConcurrent(Function<? super T, ? extends K> classifier);
    public static <T, K, A, D>
    Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier,
                                                              Collector<? super T, A, D> downstream);
    public static <T, K, A, D, M extends ConcurrentMap<K, D>>
    Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier,
                                            Supplier<M> mapFactory,
                                            Collector<? super T, A, D> downstream);

    /**
     * 該方法將流中的元素按照給定的校驗規則的結果分爲兩個部分,放到一個map中返回,map的鍵是Boolean類型,值爲元素的列表List。
     */
    public static <T>
    Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate);
    public static <T, D, A>
    Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
                                                    Collector<? super T, A, D> downstream);

    /**
     * 根據給定的鍵生成器和值生成器生成的鍵和值保存到一個map中返回,鍵和值的生成都依賴於元素,
     * 可以指定出現重複鍵時的處理方案和保存結果的map
     */
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper);
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction);
    public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                             Function<? super T, ? extends U> valueMapper,
                             BinaryOperator<U> mergeFunction,
                             Supplier<M> mapSupplier);

    /**
     * 併發版本的toMap
     */
    public static <T, K, U>
    Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
                                                        Function<? super T, ? extends U> valueMapper);

    public static <T, K, U>
    Collector<T, ?, ConcurrentMap<K,U>>
    toConcurrentMap(Function<? super T, ? extends K> keyMapper,
                    Function<? super T, ? extends U> valueMapper,
                    BinaryOperator<U> mergeFunction);

    public static <T, K, U, M extends ConcurrentMap<K, U>>
    Collector<T, ?, M> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
                                       Function<? super T, ? extends U> valueMapper,
                                       BinaryOperator<U> mergeFunction,
                                       Supplier<M> mapSupplier);

    /**
     * 這三個方法適用於彙總的,返回值分別是IntSummaryStatistics,LongSummaryStatistics,DoubleSummaryStatistics
     */
    public static <T>
    Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper);
    
    public static <T>
    Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper);
    
    public static <T>
    Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper);
}

       爲了加深大家的理解。接下來我們對Collectors裏面的每個函數都寫一個簡單的實例。

3.1.2.1 toCollection

       將流中的元素全部放置到一個集合中返回,這裏使用Collection,泛指多種集合。

    @Test
    public void toCollection() {
        List<String> list = Arrays.asList("java", "ios", "c");
        LinkedList<String> retList = list.stream().collect(Collectors.toCollection(
                new Supplier<LinkedList<String>>() {

                    @Override
                    public LinkedList<String> get() {
                        return new LinkedList<>();
                    }
                }));
    }

3.1.2.2 toList

       將流中的元素放置到一個列表集合中去。這個列表默認爲ArrayList。

    @Test
    public void collectList() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> intList = stream.collect(Collectors.toList());
    }

3.1.2.3 toSet

       將流中的元素放置到一個無序集set中去。默認爲HashSet。

    @Test
    public void toSet() {
        List<String> list = Arrays.asList("java", "ios", "c");
        Set<String> retList = list.stream().collect(Collectors.toSet());
    }

3.1.2.4 joining

       joining的目的是將流中的元素全部以字符序列的方式連接到一起,可以指定連接符,甚至是結果的前後綴。

    @Test
    public void joining() {
        List<String> list = Arrays.asList("java", "ios", "c");
        String ret = list.stream().collect(Collectors.joining(":", "@@", "@@"));
        System.out.println(ret);//@@java:ios:c@@
    }

3.1.2.5 mapping

       這個映射是首先對流中的每個元素進行映射,即類型轉換,然後再將新元素以給定的Collector進行歸納。

    @Test
    public void mapping() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // 先把流裏面的每個元素的前後加上[],之後在用:拼接起來
        String ret = list.stream().collect(Collectors.mapping(
                new Function<String, String>() {
                    @Override
                    public String apply(String s) {
                        return "[" + s + "]";
                    }
                },
                Collectors.joining(":")));
        System.out.println(ret);//[java]:[ios]:[c]
    }

3.1.2.6 collectingAndThen

       該方法是在歸納動作結束之後,對歸納的結果進行再處理。

    @Test
    public void collectingAndThen() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // Collectors.toList()之後在把List<String>通過Joiner轉換String
        String ret = list.stream().collect(Collectors.collectingAndThen(
                Collectors.toList(),
                new Function<List<String>, String>() {
                    @Override
                    public String apply(List<String> strings) {
                        return Joiner.on("; ")
                                .join(strings);
                    }
                }));
        System.out.println(ret);//java; ios; c
    }

3.1.2.7 counting

       計算流裏面的元素個數。

    @Test
    public void counting() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // 元素個數
        Long ret = list.stream().collect(Collectors.counting());
        System.out.println(ret);//3
    }

3.1.2.8 minBy/maxBy

       生成一個用於獲取最小/最大值的Optional結果的Collector。

    @Test
    public void minBy() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // 這裏簡單的用字符串比較的大小
        Optional<String> ret = list.stream().collect(Collectors.minBy(
                new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
                        return o1.compareTo(o2);
                    }
                }));
    }

3.2.2.9 summingInt/summingLong/summingDouble

       生成一個用於求元素和的Collector,首先通過給定的mapper將元素轉換類型,然後再求和。

    @Test
    public void summingInt() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 求和
        Integer ret = list.stream().collect(Collectors.summingInt(
                new ToIntFunction<Integer>() {
                    @Override
                    public int applyAsInt(Integer value) {
                        return value;
                    }

                }));
    }

3.2.2.10 averagingInt/averagingLong/averagingDouble

       生成一個用於求元素平均值的Collector,首選通過參數將元素轉換爲指定的類型。

    @Test
    public void averagingInt() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 求平均值
        Double ret = list.stream().collect(Collectors.averagingInt(
                new ToIntFunction<Integer>() {
                    @Override
                    public int applyAsInt(Integer value) {
                        return value;
                    }

                }));
    }

3.1.2.11 reducing

       reducing方法有三個重載方法,其實是和Stream裏的三個reduce方法對應的,二者是可以替換使用的,作用完全一致,也是對流中的元素做統計歸納作用(規約)。

    @Test
    public void reducing() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 求平均值
        Integer ret = list.stream().collect(Collectors.reducing(
                10,
                new Function<Integer, Integer>() {
                    @Override
                    public Integer apply(Integer integer) {
                        return integer * integer;
                    }
                },
                new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        return integer + integer2;
                    }
                }));

        System.out.println(ret); // 10 + 1*1 + 2*2 + 3*3 = 24
    }

3.1.2.12 groupingBy

       這個方法是用於生成一個擁有分組功能的Collector,它也有三個重載方法。這裏要稍微解釋下參數最長的函數每個參數的含義。

  • Function<? super T, ? extends K> classifier:確定怎麼從流裏面每個元素獲取key。
  • Supplier mapFactory:用於創建最終要生成的對象一般都是Map。
  • Collector<? super T, A, D> downstream:每一組數據用什麼來存放,一般是List,所以這個參數一般是Collectors.toList()。
    @Test
    public void collectGroupingBy() {
        List<Student> list = Arrays.asList(new Student("吳六", 26), new Student("張三", 26), new Student("李四", 27));
        Map<Integer, List<Student>> ret = list.stream().collect(Collectors.groupingBy(
                new Function<Student, Integer>() {
                    @Override
                    public Integer apply(Student student) {
                        return student.getAge();
                    }
                },
                new Supplier<Map<Integer, List<Student>>>() {
                    @Override
                    public Map<Integer, List<Student>> get() {
                        return new HashMap<>();
                    }
                },
                Collectors.toList()));

        for (Map.Entry<Integer, List<Student>> entry : ret.entrySet()) {
            Integer mapKey = entry.getKey();
            List<Student> mapValue = entry.getValue();
            System.out.println(mapKey + ":" + mapValue);
        }

    }


    private static class Student {
        private String name;
        private int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

groupingByConcurrent和groupingBy是一樣的,只不過groupingByConcurrent生成的是ConcurrentMap,而groupingBy生成的是Map。

3.1.2.13 partitioningBy

       該方法將流中的元素按照給定的校驗規則的結果分爲兩個部分,放到一個map中返回,map的鍵是Boolean類型,值爲元素的列表List。

    @Test
    public void partitioningBy() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 求平均值
        Map<Boolean, List<Integer>> ret = list.stream()
                .collect(Collectors.partitioningBy(
                        new Predicate<Integer>() {
                            @Override
                            public boolean test(Integer integer) {
                                return integer % 2 == 0;
                            }
                        },
                        Collectors.toList()));

        for (Map.Entry<Boolean, List<Integer>> entry : ret.entrySet()) {
            Boolean mapKey = entry.getKey();
            List<Integer> mapValue = entry.getValue();
            System.out.println(mapKey + ":" + mapValue);
        }
    }

3.1.2.14 toMap

       toMap方法是根據給定的鍵生成器和值生成器生成的鍵和值保存到一個map中返回,鍵和值的生成都依賴於元素,可以指定出現重複鍵時的處理方案和保存結果的map。

    @Test
    public void toMap() {
        List<Student> list = Arrays.asList(new Student("吳六", 26), new Student("張三", 26), new Student("李四", 27));
        Map<Integer, List<Student>> ret = list.stream()
                .collect(Collectors.toMap(
                        // key
                        new Function<Student, Integer>() {
                            @Override
                            public Integer apply(Student student) {
                                return student.getAge();
                            }
                        },
                        // value
                        new Function<Student, List<Student>>() {


                            @Override
                            public List<Student> apply(Student student) {
                                return Lists.newArrayList(student);
                            }
                        },
                        // 有系統key的value怎麼處理
                        new BinaryOperator<List<Student>>() {

                            @Override
                            public List<Student> apply(List<Student> students, List<Student> students2) {
                                students.addAll(students2);
                                return students;
                            }
                        }));

        for (Map.Entry<Integer, List<Student>> entry : ret.entrySet()) {
            Integer mapKey = entry.getKey();
            List<Student> mapValue = entry.getValue();
            System.out.println(mapKey + ":" + mapValue);
        }

    }


    private static class Student {
        private String name;
        private int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

3.1.2.15 summarizingInt/summarizingLong/summarizingDouble

       這三個方法適用於彙總的,返回值分別是IntSummaryStatistics,LongSummaryStatistics,DoubleSummaryStatistics。

    @Test
    public void summarizingInt() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        IntSummaryStatistics ret = list.stream()
                .collect(Collectors.summarizingInt(
                        new ToIntFunction<Integer>(){
                            @Override
                            public int applyAsInt(Integer value) {
                                return value;
                            }
                        }));
        System.out.println(ret.getAverage());
    }

       關於JDK8裏面Stream部分內容我們就講這麼些,真心希望能幫助上大家。大家如果有什麼疑問可以留言。最後,上面所有的實例代碼在 https://github.com/tuacy/google-guava-study裏面都可以找到。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章