Java8: Stream Collector

寫在前面: 本文要介紹的是流收集器.在前面的文章中,咱們都知道若是要收集流數據,調用collect方法便可.本文主要是介紹經常使用的流收集器和自定義流收集器.java

1 Common Stream Collectors

說一說經常使用的流收集器,這裏只是簡單介紹用法,具體是如何實現的我會在下面進行解釋.ide

1.1 joining

joining是用來鏈接字符串的流收集器.有三個重載方法.函數

// method definition
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);
// examples
String res1 = Stream.of("1", "2", "3", "4").collect(joining());             //1234
String res2 = Stream.of("1", "2", "3", "4").collect(joining(","));          //1,2,3,4
String res3 = Stream.of("1", "2", "3", "4").collect(joining(",", "{", "}"));//{1,2,3,4}

1.2 groupingBy

顧名思義,進行分組,也能夠多級分組.多級分組是根據上一次分組的結果來進行分組.優化

// entity
public class Person {
    private String name;
    private int age;
    private double height;
}
// examples
// e1: group by age
Map<Integer, List<Person>> groupByAge = list.stream()
.collect(groupingBy(Person::getAge));
// e2: group by age and name
Map<Integer, Map<String, List<Person>>> groupByAgeAndName = list.stream()
.collect(groupingBy(Person::getAge, groupingBy(Person::getName)));
// e3: group by age , name and height
Map<Integer, Map<String, Map<Double, List<Person>>>> groupByAgeAndNameAndHeight = list.stream()
.collect(groupingBy(Person::getAge, groupingBy(Person::getName, groupingBy(Person::getHeight))));

1.3 partition

分區是分組的特殊狀況,由一個謂詞(返回一個布爾值的函數)做爲分類函數.因此返回的Map集合只有兩個key,一個true,一個false.code

// is age greater than 20
Map<Boolean, List<Person>> isGT20 = list.stream().collect(partitioningBy(e -> e.getAge() > 20));
// is age greater than 20, and group by age
Map<Boolean, Map<Integer, List<Person>>> isGT20AndGroupByAge = list.stream().collect(partitioningBy(e -> e.getAge() > 20, groupingBy(Person::getAge)));

2 Custom Stream Collector

首先我們看collect方法的定義:對象

<R, A> R collect(Collector<? super T, A, R> collector);

collect方法接受一個Collector子類對象.咱們以前調的toList,groupingBy,partition等等都是Collectors中經過工廠方法建立的流收集器.因此若是咱們須要建立本身的流收集器,只須要實現Collector接口便可.先看Collector接口的定義,以及解釋其抽象方法的意思:接口

public interface Collector<T, A, R> {
    // 創建新的結果容器.也就是最終流元素進行處理以後的結果是存放在這個容器中的
    Supplier<A> supplier();

    // 將元素添加到結果容器中
    BiConsumer<A, T> accumulator();

    // 合併兩個結果容器,使用parallelStream的時候會調用這個方法
    BinaryOperator<A> combiner();

    // 對結果容器應用最終轉換,是累計過程當中最後要調的一個函數,做用相似與Stream的map方法
    Function<A, R> finisher();

    // 返回一個不可變的Characteristic集合,它定義了收集器的行爲
    // 尤爲是關於流是否能夠並行規約,以及使用哪些優化的提示
    Set<Characteristics> characteristics();
}

2.1 Example

如今咱們須要實現對一個Person對象集合按年齡來分組,實現代碼以下:字符串

// define a custom collector
public class MyGrouping implements Collector<Person, Map<Integer, ArrayList<Person>>, Map<Integer, ArrayList<Person>>> {
    @Override
    public Supplier<Map<Integer, ArrayList<Person>>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<Integer, ArrayList<Person>>, Person> accumulator() {
        return (map, p) -> {
            ArrayList<Person> list;
            if ((list = map.get(p.getAge())) != null) {
                list.add(p);
            } else {
                list = new ArrayList<>();
                list.add(p);
                map.put(p.getAge(), list);
            }
        };
    }

    @Override
    public BinaryOperator<Map<Integer, ArrayList<Person>>> combiner() {
        return (m1, m2) -> Stream.of(m1, m2)
                .map(Map::entrySet)
                .flatMap(Collection::stream)
                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> {
                    e1.addAll(e2);
                    return e1;
                }));
    }

    @Override
    public Function<Map<Integer, ArrayList<Person>>, Map<Integer, ArrayList<Person>>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
    }
}

// how to use
Map<Integer, ArrayList<Person>> customGroupByAge = list.stream().collect(new MyGrouping());

3 Summary

  • collect是一個終端操做,接受一個收集器對象
  • 收集器能夠高效地複合起來,進行多級分組,分區和歸約
  • 能夠實現Collector接口來實現本身的收集器
相關文章
相關標籤/搜索