stream and lambda(18) - 終止操作之 stream 收集器 collectors

語言: CN / TW / HK

先定義初始值

private Stream<String> getStrStream() {
    return Stream.of("this", "is", "the", "the", "test", null);
}

各種方法使用如下:

public void collectorTest() {
    //Collectors.toCollection 傳入一個承載結果的 Supplier
    ArrayList<String> toCollection = getStrStream().collect(Collectors.toCollection(ArrayList::new));
    System.out.println("Collectors.toCollection 轉成 ArrayList 結果:" + toCollection);

    //Collectors.toList
    List<String> toList = getStrStream().collect(Collectors.toList());
    System.out.println("Collectors.toList 結果:" + toList);

    //Collectors.toSet
    Set<String> toSet = getStrStream().collect(Collectors.toSet());
    System.out.println("Collectors.toSet 去重後結果:" + toList);

    //Collectors.toMap 前兩個參數,分別是 key 和 value 的處理
    //key 如果有重複會報錯,value 如果是 null 也會報錯
    //Map<String, Integer> toMap = strings.stream().collect(Collectors.toMap(
    //        Function.identity(), s -> s == null ? null : s.length()));

    //第三個參數是合併函數,我們可以用這個來去重
    Map<String, Integer> toMap = getStrStream().collect(Collectors.toMap(
            Function.identity(),
            s -> s == null ? 0 : s.length(),
            (k1, k2) -> k2));
    System.out.println("Collectors.toMap 默認 HashMap 亂序結果:" + toMap);

    //第四個參數是指定承載元素的Map,默認是使用HashMap,不過我們可以根據情況改
    toMap = getStrStream().collect(Collectors.toMap(
            Function.identity(),
            s -> s == null ? 0 : s.length(),
            (k1, k2) -> k2,
            LinkedHashMap::new));
    System.out.println("Collectors.toMap LinkedHashMap 有序結果:" + toMap);

    //toConcurrentMap 和 toMap 用法基本相同
    //注意 ConcurrentHashMap 本身 key 和 value 都不可以為null
    ConcurrentMap<String, Integer> toConcurrentMap = getStrStream().collect(Collectors.toConcurrentMap(
            //使用 Function.identity() 會報NPE
            String::valueOf,
            s -> s == null ? 0 : s.length(),
            (k1, k2) -> k2));
    System.out.println("Collectors.toConcurrentMap 結果:" + toConcurrentMap);

    // 等效於 Stream.count
    Long count = getStrStream().collect(Collectors.counting());
    System.out.println("Collectors.counting 計數結果:" + count);

    // joining 可帶分隔符和左右的開始終止符
    String join = getStrStream().collect(Collectors.joining());
    System.out.println("Collectors.joining 無分隔符結果:" + join);
    join = getStrStream().collect(Collectors.joining(","));
    System.out.println("Collectors.joining 有分隔符結果:" + join);
    join = getStrStream().collect(Collectors.joining(",", "[", "]"));
    System.out.println("Collectors.joining 有分隔符和左右護法結果:" + join);

    // 等效於 Stream.min
    Optional<String> min = getStrStream().map(String::valueOf)
            .collect(Collectors.minBy(Comparator.naturalOrder()));
    System.out.println("Collectors.minBy 結果:" + min.orElse(""));

    // 等效於 Stream.max
    Optional<String> max = getStrStream().map(String::valueOf)
            .collect(Collectors.minBy(Comparator.reverseOrder()));
    System.out.println("Collectors.maxBy 結果:" + max.orElse(""));

    //等效於 Stream.reduce
    String reduce = getStrStream().collect(Collectors.reducing("", (s1, s2) -> s1 + "\t" + s2));
    System.out.println("Collectors.reducing 結果:" + reduce);

    Double average = getStrStream().collect(
                Collectors.averagingInt(value -> String.valueOf(value).length()));
    System.out.println("Collectors.averagingInt 平均值結果:" + average);

    IntSummaryStatistics summarizingInt = getStrStream().collect(Collectors.summarizingInt(value -> String.valueOf(value).length()));
    System.out.println("Collectors.summarizingInt 結果:" + summarizingInt);

    Integer collectingAndThen = getStrStream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
    System.out.println("Collectors.collectingAndThen 先轉List再取size 結果:" + collectingAndThen);

    //等效於 Stream.map().collect()
    List<String> mapping = getStrStream().collect(
            Collectors.mapping(String::valueOf, Collectors.toList()));
    System.out.println("Collectors.mapping 結果:" + mapping);

    Map<Integer, List<String>> groupingBy = getStrStream().collect(
            Collectors.groupingBy(s -> String.valueOf(s).length()));
    System.out.println("Collectors.groupingBy 按字母個數分組結果:" + groupingBy);

    Map<Integer, Long> groupingBy2 = getStrStream().collect(
            Collectors.groupingBy(s -> String.valueOf(s).length(), Collectors.counting()));
    System.out.println("Collectors.groupingBy 按字母個數分組,分組統計個數,結果:" + groupingBy2);

    LinkedHashMap<Integer, Long> groupingBy3 = getStrStream().collect(
            Collectors.groupingBy(s -> String.valueOf(s).length(),
                    LinkedHashMap::new, Collectors.counting()));
    System.out.println("Collectors.groupingBy 按字母個數分組,分組統計個數,存放在LinkedHashMap結果:" + groupingBy3);

    //groupingByConcurrent 和 groupingBy 用法是基本是一樣的
    Map<Integer, List<String>> groupingByConcurrent = getStrStream().collect(
            Collectors.groupingByConcurrent(s -> String.valueOf(s).length()));
    System.out.println("Collectors.groupingByConcurrent 按字母個數分組結果:" + groupingByConcurrent);

    //partitioningBy 和 groupingBy 的主要區別是,只能按布爾類型來分組
    Map<Boolean, List<String>> partitioningBy = getStrStream().collect(
            Collectors.partitioningBy(s -> String.valueOf(s).length() > 3));
    System.out.println("Collectors.partitioningBy 按字母長度分塊,結果:" + partitioningBy);

    Map<Boolean, Long> partitioningBy2 = getStrStream().collect(Collectors.partitioningBy(s -> String.valueOf(s).length() > 3, Collectors.counting()));
    System.out.println("Collectors.partitioningBy 按字母個數分塊並統計結果:" + partitioningBy2);
}

輸出如下:

Collectors.toCollection 轉成 ArrayList 結果:[this, is, the, the, test, null]

Collectors.toList 結果:[this, is, the, the, test, null]

Collectors.toSet 去重後結果:[this, is, the, the, test, null]

Collectors.toMap 默認 HashMap 亂序結果:{null=0, the=3, test=4, this=4, is=2}

Collectors.toMap LinkedHashMap 有序結果:{this=4, is=2, the=3, test=4, null=0}

Collectors.toConcurrentMap 結果:{the=3, test=4, null=0, this=4, is=2}

Collectors.counting 計數結果:6

Collectors.joining 無分隔符結果:thisisthethetestnull

Collectors.joining 有分隔符結果:this,is,the,the,test,null

Collectors.joining 有分隔符和左右護法結果:[this,is,the,the,test,null]

Collectors.minBy 結果:is

Collectors.maxBy 結果:this

Collectors.reducing 結果: this is the the test null

Collectors.averagingInt 平均值結果:3.3333333333333335

Collectors.summarizingInt 結果:IntSummaryStatistics{count=6, sum=20, min=2, average=3.333333, max=4}

Collectors.collectingAndThen 先轉List再取size 結果:6

Collectors.mapping 結果:[this, is, the, the, test, null]

Collectors.groupingBy 按字母個數分組結果:{2=[is], 3=[the, the], 4=[this, test, null]}

Collectors.groupingBy 按字母個數分組,分組統計個數,結果:{2=1, 3=2, 4=3}

Collectors.groupingBy 按字母個數分組,分組統計個數,存放在LinkedHashMap結果:{4=3, 2=1, 3=2}

Collectors.groupingByConcurrent 按字母個數分組結果:{2=[is], 3=[the, the], 4=[this, test, null]}

Collectors.partitioningBy 按字母長度分塊,結果:{false=[is, the, the], true=[this, test, null]}

Collectors.partitioningBy 按字母個數分塊並統計結果:{false=3, true=3}

總結

  • Collectors 提供的部分收集器,其實使用部分終止操作方法更簡單。
  • Collectors 提供的收集器,基礎上可以滿足我們日常的編碼需求,如果有特殊情況,可以直接使用 collect 方法傳普通參數。
  • 部分收集器,組合起來使用,效果更好。
  • 收集器的使用,還是要多練習才能更好的掌握。