Java stream的collect和Collector

最近在使用Java8的新特性stream,因为LZ也在使用Spark,所以这种流式编程还是蛮顺手的,比以前的循环处理方便很多。stream基本用法网上有很多教程,这里就不在赘述了。这里LZ重点介绍stream的collect方法和其强大的工具类Collector。
先看下接口中声名的collect方法:

<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);

第一个方法比较常用,比如将stream转化为集合:

Stream<String> stream = Stream.of("I", "love", "you", "too");
List<String> list = stream.collect(Collectors.toList());

其中Collectors类中提供了许多Collector接口的默认实现,这些实现能满足大部分的需求,否则要自己来实现Collector接口。Collector接口的3个泛型分别表示:

  • T:输入元素类型(上例中的集合元素类型String)
  • A:缩减操作的可变累积类型(上例中不存在)
  • R:可变减少操作的结果类型(上例中的集合类型List)
    这个方法是将N个输入元素T转化为1个输出元素R的过程,比如算平均数:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
double d = stream.collect(Collectors.averagingInt(i -> i));

还有更复杂的groupby操作:

// Student {id, classId, score}
List<Student> list = ...;
Map<Integer, Double> map = 
    list.stream().collect(Collectors.groupingBy(Student::getClassId, 
    Collectors.averagingInt(Student::getScore)));

第二个方法稍微复杂些,下面是该方法几个参数的接口:

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

supplier用来创建最终的结果类型R(第一个例子中的List)

@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);
        return (l, r) -> {
            accept(l, r);
            after.accept(l, r);
        };
    }
}

accumulator和combiner都实现了上面的接口,accumulator用来将输入元素转化为中间元素(第一个例子中将String插入List,T->A。这个例子中的中间元素和结果元素的类型相同)。combiner用来将中间元素转化为结果元素(第一个例子中将多个List合并为一个List,A->R),下图为整个合并的过程:
avatar
用这种方式改写下第一个例子:

Stream<String> stream = Stream.of("I", "love", "you", "too");
List<String> list = stream.collect(() -> {
            return new ArrayList<String>();
        }, (List<String> list0, String str) -> {
            list0.add(str);
        }, (List<String> list1, List<String> list2) -> {
            list1.addAll(list2);
        });

2 Replies to “Java stream的collect和Collector”

发表评论