Java Stream的終止操作(三)

Stream Head的構(gòu)建(一)
Stream 的中間操作(二)
上兩篇完成了源頭和中間流的構(gòu)造岩喷,這篇看看終止操作forEach是如何將整個(gè)過程串起來的谣光。

List<String> names= Arrays.asList("one", "two", "three", "four");
        names.stream()
                .filter(s -> s.length() > 2)
                .map(String::toUpperCase)
                .forEach(System.out::println);

從上文可以看出forEach是在stream2上調(diào)用的

ReferencePipeline.java
public void forEach(Consumer<? super P_OUT> action) {
        evaluate(ForEachOps.makeRef(action, false));
    }

首先工具類ForEachOps使用Consumer構(gòu)建一個(gè)終止操作作為evaluate參數(shù)

ForEachOps.java
public static <T> TerminalOp<T, Void> makeRef(Consumer<? super T> action,  boolean ordered) {
       ...
        //OfRef說明是引用類型厌秒,還有OfInt等基本類型淌铐,都是對ForEachOp在特定數(shù)據(jù)類型上定制
        return new ForEachOp.OfRef<>(action, ordered);
    }
//OfRef 是ForEachOp子類
 OfRef(Consumer<? super T> consumer, boolean ordered) {
                super(ordered);
                this.consumer = consumer;
      }

ForEachOp和OfRef都比較簡潔,看幾個(gè)主要方法

ForEachOp實(shí)現(xiàn)了TerminalOp的evaluateSequential和TerminalSink的get方法
 public <S> Void evaluateSequential(PipelineHelper<T> helper,Spliterator<S> spliterator){
          //this指的就是ForEachOp.OfRef
          //下文能看到helper.wrapAndCopyInto(this, spliterator)返回的就是this
          //因?yàn)椴恍枰祷刂担詆et方法返回的就是下面的null,。
            return helper.wrapAndCopyInto(this, spliterator).get();
        }

  public Void get() {
            return null;
        }
//OfRef 實(shí)現(xiàn)TerminalSink的accept
   public void accept(T t) {
        //此例consumer就是打印的方法
          consumer.accept(t);
        }

接下來是evaluate方法的調(diào)用

AbstractPipeline.java
final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
       ...
        return isParallel()
               ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
              //非并行,走的是這個(gè)方法
              :terminalOp.evaluateSequential(this,sourceSpliterator(terminalOp.getOpFlags()));
    }

先看sourceSpliterator方法

private Spliterator<?> sourceSpliterator(int terminalFlags) {
        Spliterator<?> spliterator = null;
        if (sourceStage.sourceSpliterator != null) {
        // 上篇說為什么中間流要有源頭的引用占哟,作用在這里,通過源頭的引用酿矢,獲取迭代器.
            spliterator = sourceStage.sourceSpliterator;
        //置為空榨乎,下次再調(diào)用進(jìn)入else拋出異常
            sourceStage.sourceSpliterator = null;
        }
        else if (sourceStage.sourceSupplier != null) {
           ...
        }
        else {
          //防止再次調(diào)用
            throw new IllegalStateException(MSG_CONSUMED);
        }

       ...
        return spliterator;
    }

接下來帶著兩個(gè)參數(shù)進(jìn)入上文所說的evaluateSequential方法,真正實(shí)現(xiàn)方法是helper.wrapAndCopyInto(this, spliterator)

AbstractPipeline.java
final <P_IN, S extends Sink<E_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator) {
        copyInto(wrapSink(Objects.requireNonNull(sink)), spliterator);
        return sink;
    }

 public final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
       ...
        //此處AbstractPipeline類型的 p就是map方法返回的stream2,
        for ( AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
        //參數(shù)sink就是上面的ForEachOp.OfRef
        //循環(huán)起來形成的就是單向鏈表
            sink = p.opWrapSink(..., sink);
        }
        return (Sink<P_IN>) sink;
    }

再看一下雙向鏈表的數(shù)據(jù)結(jié)構(gòu)瘫筐,Sink是這樣串起來的


stream wrap_sink.png

回到中間流的opWrapSink方法蜜暑,發(fā)現(xiàn)返回的實(shí)例是ChainedReference

Sink.java
//Sink接口是Comsume的子類,ChainedReference實(shí)現(xiàn)了Sink接口策肝,里面的方法先不用管
static abstract class ChainedReference<T, E_OUT> implements Sink<T> {
        protected final Sink<? super E_OUT> downstream;
        //以下游Sink為參數(shù)肛捍,并持有引用
        public ChainedReference(Sink<? super E_OUT> downstream) {
            this.downstream = Objects.requireNonNull(downstream);
        }

        @Override
        public void begin(long size) {
            downstream.begin(size);
        }

        @Override
        public void end() {
            downstream.end();
        }
        ...
    }

到此中間流對源元素的操作方法就從尾到頭串起來了,只差最后一步copyInto(sink)

AbstractPipeline.java
 @Override
          //wrappedSink就是上面wrapSink返回值
    final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
       ...
        if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
            //這里才開始所有的計(jì)算。begin 先通知各Sink開始準(zhǔn)備
            wrappedSink.begin(spliterator.getExactSizeIfKnown());
            //如第一篇所述驳糯,調(diào)用迭代器此方法發(fā)射元素
            spliterator.forEachRemaining(wrappedSink);
            //通知結(jié)束
            wrappedSink.end();
        }
        else {
           ...
        }
    }

//wrappedSink.begin 就是filter方法中Sink.ChainedReference復(fù)寫的begin
   public void begin(long size) {
         downstream.begin(-1);
    }
  //然后調(diào)用map方法中Sink.ChainedReference的begin ,沒有復(fù)寫
   public void begin(long size) {
            downstream.begin(size);
    }
//最后是OfRef繼承的begin
    default void begin(long size) {}

返回看看ArrayListSpliterator迭代器的遍歷

ArrayList.java
 public void forEachRemaining(Consumer<? super E> action) {
            int i, hi, mc; // hoist accesses and checks from loop
            ArrayList<E> lst; Object[] a;
            if (action == null)
                throw new NullPointerException();
            if ((lst = list) != null && (a = lst.elementData) != null) {
                if ((hi = fence) < 0) {
                    mc = lst.modCount;
                    hi = lst.size;
                }
                else
                    mc = expectedModCount;
                if ((i = index) >= 0 && (index = hi) <= a.length) {
                    for (; i < hi; ++i) {
                        @SuppressWarnings("unchecked") E e = (E) a[i];
                        //把元素交給Sink
                        action.accept(e);
                    }
                    if (lst.modCount == mc)
                        return;
                }
            }
            throw new ConcurrentModificationException();
        }

如前面所述Sink是Consumer子類氢橙,實(shí)現(xiàn)了accept方法

 //wrappedSink.accept就是filter方法中Sink.ChainedReference復(fù)寫的accept
  public void accept(P_OUT u) {
    //如果滿足條件就會調(diào)用下游map的同名方法
      if (predicate.test(u))
             downstream.accept(u);
      }
  //然后是map的accept 酝枢,先映射,再調(diào)用下游accept
   public void accept(P_OUT u) {
             downstream.accept(mapper.apply(u));
      }
//最后是OfRef的accept悍手,
   public void accept(T t) {
            //到此被打印出來
             consumer.accept(t);
     }
//雖然accept方法屬于不同流帘睦,卻能被一次調(diào)用,這是性能所在坦康。

至此竣付,Java 流式構(gòu)建和操作的基本框架就顯示出來了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滞欠,一起剝皮案震驚了整個(gè)濱河市古胆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖逸绎,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惹恃,死亡現(xiàn)場離奇詭異,居然都是意外死亡棺牧,警方通過查閱死者的電腦和手機(jī)巫糙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颊乘,“玉大人参淹,你說我怎么就攤上這事》η模” “怎么了浙值?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纲爸。 經(jīng)常有香客問我亥鸠,道長,這世上最難降的妖魔是什么识啦? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任负蚊,我火速辦了婚禮,結(jié)果婚禮上颓哮,老公的妹妹穿的比我還像新娘家妆。我一直安慰自己,他們只是感情好冕茅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布伤极。 她就那樣靜靜地躺著,像睡著了一般姨伤。 火紅的嫁衣襯著肌膚如雪哨坪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天乍楚,我揣著相機(jī)與錄音当编,去河邊找鬼。 笑死徒溪,一個(gè)胖子當(dāng)著我的面吹牛忿偷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臊泌,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鲤桥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了渠概?” 一聲冷哼從身側(cè)響起茶凳,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后慧妄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顷牌,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年塞淹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窟蓝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饱普,死狀恐怖运挫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情套耕,我是刑警寧澤谁帕,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站冯袍,受9級特大地震影響匈挖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜康愤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一儡循、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧征冷,春花似錦择膝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叔收,卻和暖如春齿穗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饺律。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工窃页, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蓝晒。 一個(gè)月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓腮出,卻偏偏與公主長得像帖鸦,于是被迫代替她去往敵國和親芝薇。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355