Java Stream 收集器(四)

上篇是将符合条件的流元素加工后打印出来,这篇讲述如何收集起来。与上篇相比只有最后一个方法不一样

List<String> names= Arrays.asList("one", "two", "three", "four");
List<String> result = names.stream()
                .filter(s -> s.length() > 2)
                .map(String::toUpperCase)
                .collect(Collectors.toList());

Collectors是个工具类,里面有多种方法来实现Collector这个接口:像本例中的Collectors.toList()。

Collectors.java
 //CH_ID 表示流的特性是Collector.Characteristics.IDENTITY_FINISH
public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },CH_ID);
    }

Collector接口提供四个函数,相互配合将输入的条目累加到可变容器中,还能对容器中的结果来一个转化后返回,如果对函数式接口不熟悉,可以参考这篇

Collector.java
 /**
 *Collector<T, A, R>  泛型接口 
 * T 元素类型  本例是String
*  A 归并操作中可累积类型 本例是ArrayList
 * R 归并结束后最终返回类型 本例与A一样
*/
//从名称看就是一个提供者
Supplier<A> supplier();
//从Consumer(消费者)延伸而来,Bi表示两,BiConsumer表示消费两个参数:一个是类型A,一个是类型T 。
BiConsumer<A, T> accumulator();
//相当于BiFunction<T,T,T> 即参数类型和返回类型一样
BinaryOperator<A> combiner();
//Function 表示消费A输出R,也就是对结果转化。本例返回类型R和A一样
Function<A, R> finisher();

CollectorImpl是Collectors的内部类,实现了Collector接口,比较简单,就是用参数初始化了字段,并提供方法获取

Collectors.java
static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
        private final Supplier<A> supplier;
        private final BiConsumer<A, T> accumulator;
        private final BinaryOperator<A> combiner;
        private final Function<A, R> finisher;
        private final Set<Characteristics> characteristics;

        CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Function<A,R> finisher,
                      Set<Characteristics> characteristics) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.combiner = combiner;
            this.finisher = finisher;
            this.characteristics = characteristics;
        }
        //本例使用这个构造方法,如上所说返回类型R和容器类型A一样,因为是泛型,用castingIdentity()方法强转了一下
        CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Set<Characteristics> characteristics) {
            this(supplier, accumulator, combiner, castingIdentity(), characteristics);
        }
        @Override
        public BiConsumer<A, T> accumulator() {
            return accumulator;
        }
        @Override
        public Supplier<A> supplier() {
            return supplier;
        }
        @Override
        public BinaryOperator<A> combiner() {
            return combiner;
        }
        @Override
        public Function<A, R> finisher() {
            return finisher;
        }
       ...
    }

再看collect方法的调用,它是在map方法返回的stream2上调用的

ReferencePipeline.java
public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
        A container;
        if (...) {
            ...
        }
        else {
          //我们是串行流 走这个
            container = evaluate(ReduceOps.makeRef(collector));
        }
        return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
               ? (R) container  //构造Collector已设置其特性就是IDENTITY_FINISH,所以返回这个
               : collector.finisher().apply(container);
    }

ReduceOps也是一个工具类,这里使用上面的Collector作为参数,创建终结操作的实例

ReduceOps.java
public static <T, I> TerminalOp<T, I>  makeRef(Collector<? super T, I, ?> collector) {
        Supplier<I> supplier = Objects.requireNonNull(collector).supplier();
        BiConsumer<I, ? super T> accumulator = collector.accumulator();
        BinaryOperator<I> combiner = collector.combiner();
        class ReducingSink extends Box<I>  implements AccumulatingSink<T, I, ReducingSink> {
            @Override
            public void begin(long size) {
                state = supplier.get();
            }

            @Override
            public void accept(T t) {
                accumulator.accept(state, t);
            }

            @Override
            public void combine(ReducingSink other) {
                state = combiner.apply(state, other.state);
            }
        }
        return new ReduceOp<T, I, ReducingSink>(StreamShape.REFERENCE) {
            @Override
              //ReduceOp的evaluateSequential方法会调用
            public ReducingSink makeSink() {
                return new ReducingSink();
            }
            ...
        };
    }

先看返回值ReduceOp,它是ReduceOps内部抽象类,实现了TerminalOp。此处返回的是ReduceOp子类,它实现了makesink方法

ReduceOps.java
private static abstract class ReduceOp<T, R, S extends AccumulatingSink<T, R, S>>
            implements TerminalOp<T, R> {
        private final StreamShape inputShape;
        ...
      //重要的抽象方法
        public abstract S makeSink();
         ...
        @Override
        public <P_IN> R evaluateSequential(PipelineHelper<T> helper, Spliterator<P_IN> spliterator) {
          //makeSink()返回ReducingSink
          //和前文一样,helper.wrapAndCopyInto(makeSink(), spliterator)返回makeSink的返回值,即ReducingSink
        // ReducingSink的get方法返回的是ReducingSink中的state,在begin方法中初始化
            return helper.wrapAndCopyInto(makeSink(), spliterator).get();
        }
        ...
    }

后面的流程和前一篇类似。ReducingSink的accept实现了收集操作

@Override
 public void accept(T t) {
       accumulator.accept(state, t);
  }

流遍历结束以后,就会返回ReducingSink中的state,再回顾collect方法

ReferencePipeline.java
public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
        A container;
        if (...) {
            ...
        }
        else {
          //返回的是collector中supplier提供 的内容
            container = evaluate(ReduceOps.makeRef(collector));
        }
        return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
               ? (R) container  //构造Collector已设置其特性就是IDENTITY_FINISH,所以返回这个,返回类型和容器类型一致
               : collector.finisher().apply(container);
    }

全部流程


Java Stream_collect.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容

  • Int Double Long 设置特定的stream类型, 提高性能,增加特定的函数 无存储。stream不是一...
    patrick002阅读 1,269评论 0 0
  •   在这之前,我们用过了collect终端操作了,当时主要是用来把Stream中所有的元素结合成一个List。在这...
    琼珶和予阅读 450评论 0 0
  • 原文地址: 深蓝至尊 一. 流式处理简介 在我接触到java8流式处理的时候,我的第一感觉是流式处理让集合操作变得...
    咻咻咻i阅读 1,144评论 0 0
  • 收集器简介 Collector 函数式编程相对于指令式编程的一个主要优势:你只需要指出希望的结果“做什么”,而不用...
    浔它芉咟渡阅读 816评论 0 4
  • 昨晚老公突然喊我一起去看电影。于是两个人,匆忙买了票,我把小宝喂好哄睡,私奔到月球,喔不,是电影院。 我本不想去,...
    郭小果子阅读 314评论 9 1