Slide 1

Slide 1 text

Inside Stream API Java in the Box カサレアル 櫻庭 祐一

Slide 2

Slide 2 text

さっそくですが、クイズです なんと出力される? void main() { var s = Stream.generate(() -> { IO.print("①"); return 0; }); IO.print("②"); s = s.limit(1); s = s.peek(x -> IO.print("③")); IO.println("④"); s.forEach(x -> IO.print("⑤")); } 選択肢 1. ① ② ③ ④ ⑤ 2. ② ④ ① ③ ⑤ 3. ① ② ④ ③ ⑤ 4. コンパイルエラー

Slide 3

Slide 3 text

さっそくですが、クイズです なんと出力される? void main() { var s = Stream.generate(() -> { IO.print("①"); return 0; }); IO.print("②"); s = s.limit(1); s = s.peek(x -> IO.print("③")); IO.println("④"); s.forEach(x -> IO.print("⑤")); } 選択肢 1. ① ② ③ ④ ⑤ 2. ② ④ ① ③ ⑤ 3. ① ② ④ ③ ⑤ 4. コンパイルエラー

Slide 4

Slide 4 text

Stream APIは 逐次実行されるわけではない ラムダ式を遅延実行 中間操作の戻り値の方はすべてStream 実際に使われているクラスは? 終端操作では何が行われているのか?

Slide 5

Slide 5 text

Agenda OpenJDKのソースコード Stream生成 中間操作 終端操作 まとめ

Slide 6

Slide 6 text

OpenJDKのソースコード

Slide 7

Slide 7 text

OpenJDKのソースコード GitHub: OpenJDK https://github.com/openjdk/ プロジェクトごとにレポジトリ ex) jdk, valhalla, panama, amber, et al. 各プロジェクトの成果をjdkにマージ

Slide 8

Slide 8 text

JDKレポジトリ HotSpot VM 標準クラスライブラリ ツール 複数のOSをサポート 複数のCPUをサポート

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

JDKレポジトリのディレクトリ構成 jdk ...... モジュールごと OSごと shareは共通 bin doc make src test hotspot java.base java.compiler aix linux macosx share unix windows classes conf data legal man native META-INF com java javax jdk sun パッケージ

Slide 11

Slide 11 text

Stream生成

Slide 12

Slide 12 text

ターゲットとするコード var sum = List.of(0, 1, 2, 3, 4, 5) .stream() .filter(x -> x%2 == 0) .map(x -> x*x) .reduce(0, (prv, prsnt) -> prv+prsnt); Stream生成 終端操作 中間操作 }

Slide 13

Slide 13 text

Stream生成 java.util.Collection default Stream stream() { return StreamSupport.stream(spliterator(), false); } default Spliterator spliterator() { return Spliterators.spliterator(this, 0); }

Slide 14

Slide 14 text

Spliteratorインタフェース イテレーションを行うためのインタフェース Split + Iterator パラレル 分割統治法 trySplit() シーケンシャル tryAdvance() forEachRemaining()

Slide 15

Slide 15 text

Stream生成 java.util.Collection default Stream stream() { return StreamSupport.stream(spliterator(), false); } default Spliterator spliterator() { return Spliterators.spliterator(this, 0); }

Slide 16

Slide 16 text

Spliterators.spliterator() java.util.Spliterators public static Spliterator spliterator( Collection c, int characteristics) { return new IteratorSpliterator<>(Objects.requireNonNull(c), characteristics); } ソースの種類でオーバーロード ソースの種類に対応する実装クラス

Slide 17

Slide 17 text

Spliterator IteratorSpliterator IntArraySpliterator LongArraySpliterator DoubleArraySpliterator Spliteratorインタフェースのクラス構成

Slide 18

Slide 18 text

Stream生成 java.util.Collection default Stream stream() { return StreamSupport.stream(spliterator(), false); } default Spliterator spliterator() { return Spliterators.spliterator(this, 0); }

Slide 19

Slide 19 text

StreamSupport.stream () java.util.stream.StreamSupport public static Stream stream( Spliterator spliterator, boolean parallel) { Objects.requireNonNull(spliterator); return new ReferencePipeline.Head<>( spliterator, StreamOpFlag.fromCharacteristics(spliterator), parallel); } ????

Slide 20

Slide 20 text

DoubleStream Stream Streamインタフェース関連のクラス構成 BaseStream イテレーションを行うための 基本機能を定義 LongStream IntStream

Slide 21

Slide 21 text

PipelineHelper Streamインタフェース関連のクラス構成 BaseStream Stream パイプラインを作るための 基本機能 Head AbstractPipeline ReferencePipeline 参照型用パイプライン パイプラインの先頭 Spliterator パイプラインチェーンを 構成するフィールドを定義

Slide 22

Slide 22 text

StreamSupport.stream () java.util.stream.StreamSupport public static Stream stream( Spliterator spliterator, boolean parallel) { Objects.requireNonNull(spliterator); return new ReferencePipeline.Head<>( spliterator, StreamOpFlag.fromCharacteristics(spliterator), parallel); }

Slide 23

Slide 23 text

Stream生成 まとめ ソースの要素に応じた Spliteratorオブジェクトと パイプラインの先頭オブジェクトを生成

Slide 24

Slide 24 text

中間操作

Slide 25

Slide 25 text

ラムダ式の処理順序 No.1 String hello(String who) { IO.println("Call hello()"); return "Hello, " + who + "!"; } void printMessage(String message) { IO.println("Call printMessage()"); IO.println(message); } printMessage(hello("World")); > java ExeOrder.java Call hello() Call printMessage() Hello, World!

Slide 26

Slide 26 text

ラムダ式の処理順序 No.2 void printMessage(Function func, String helloArg) { IO.println("Call printMessage()"); IO.println(func.apply(helloArg)); } printMessage(m -> hello(m), "World"); > java ExeOrder.java Call printMessage() Call hello() Hello, World!

Slide 27

Slide 27 text

ラムダ式の処理順序 No.3 Supplier printMessageLater( Function func, String helloArg) { IO.println("Call printMessageLater()"); return () -> func.apply(helloArg); } var supplier = printMessageLater(m -> hello(m), "World"); IO.println("After printMessageLater()"); IO.println(supplier.get()); > java ExeOrder.java Call printMessageLater() After printMessageLater() Call hello() Hello, World!

Slide 28

Slide 28 text

ラムダ式の処理順序 ラムダ式を保存しておくことで遅延実行が可能 さらに、ラムダ式をキャプチャするラムダ式を使えば フィールドに保持することなく遅延実行が可能

Slide 29

Slide 29 text

ターゲットとするコード var sum = List.of(0, 1, 2, 3, 4, 5) .stream() .filter(x -> x%2 == 0) .map(x -> x*x) .reduce(0, (prv, prsnt) -> prv+prsnt); 終端操作 中間操作 }

Slide 30

Slide 30 text

ReferencePipeline.map() java.util.stream.ReferencePipeline public final Stream map( Function mapper) { Objects.requireNonNull(mapper); return new StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedReference<>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.apply(u)); } };

Slide 31

Slide 31 text

ReferencePipeline.map() java.util.stream.ReferencePipeline public final Stream map( Function mapper) { Objects.requireNonNull(mapper); return new StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedReference<>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.apply(u)); } };

Slide 32

Slide 32 text

PipelineHelper Streamインタフェース関連のクラス構成 BaseStream Stream Head AbstractPipeline ReferencePipeline Spliterator StatelessOp 中間操作パイプライン

Slide 33

Slide 33 text

ReferencePipeline.map() java.util.stream.ReferencePipeline public final Stream map( Function mapper) { Objects.requireNonNull(mapper); return new StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedReference<>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.apply(u)); } };

Slide 34

Slide 34 text

AbstractPipeline.() java.util.stream.AbstractPipeline AbstractPipeline( AbstractPipeline previousStage, int opFlags) { if (previousStage.linkedOrConsumed) throw new IllegalStateException(MSG_STREAM_LINKED); previousStage.linkedOrConsumed = true; previousStage.nextStage = this; this.previousStage = previousStage; this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK; this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, prev this.sourceStage = previousStage.sourceStage; this.depth = previousStage.depth + 1; }

Slide 35

Slide 35 text

PipelineHelper Streamインタフェース関連のクラス構成 BaseStream Stream Head AbstractPipeline ReferencePipeline Spliterator StatelessOp 中間操作パイプライン previousStage nextStage

Slide 36

Slide 36 text

中間操作でのオブジェクト Head StatelessOp StatelessOp previousStage previousStage

Slide 37

Slide 37 text

ReferencePipeline.map() java.util.stream.ReferencePipeline public final Stream map( Function mapper) { Objects.requireNonNull(mapper); return new StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedReference<>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.apply(u)); } };

Slide 38

Slide 38 text

Sinkインタフェース シンク = 台所の流し場 処理を沈めておくイメージ?? Consumerインタフェースのサブインタフェース begine()/accept()/end() ChainedReference Sinkインタフェースの実装クラス フィールドにdownstreamを持つ

Slide 39

Slide 39 text

ChainedReference java.util.stream.Sink abstract static class ChainedReference implements Sink { protected final Sink downstream; public ChainedReference(Sink downstream) { this.downstream = Objects.requireNonNull(downstream); } ......

Slide 40

Slide 40 text

ReferencePipeline.map() java.util.stream.ReferencePipeline public final Stream map( Function mapper) { Objects.requireNonNull(mapper); return new StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedReference<>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.apply(u)); } }; ラムダ式をキャプチャ

Slide 41

Slide 41 text

ReferencePipeline.filter() java.util.stream.ReferencePipeline public final Stream filter( Predicate predicate) { Objects.requireNonNull(mapper); return new StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedReference<>(sink) { @Override public void accept(P_OUT u) { if (predicate.test(u)) downstream.accept(u); } };

Slide 42

Slide 42 text

中間操作でのオブジェクト Head StatelessOp StatelessOp previousStage previousStage filter map

Slide 43

Slide 43 text

中間操作 まとめ パイプラインステージのチェーンを生成 中間操作のラムダ式をSinkにキャプチャ Sinkのオブジェクトはまだ生成されていない

Slide 44

Slide 44 text

終端操作

Slide 45

Slide 45 text

終端操作 1. Sinkオブジェクトのチェーン生成 2. Sinkオブジェクトチェーンに対するイテレーション シリアル: 通常のイテレーション パラレル: 分割統治によるイテレーション

Slide 46

Slide 46 text

ReferencePipeline.reduce() java.util.stream.ReferencePipeline @Override public final P_OUT reduce( final P_OUT identity, final BinaryOperator accumulator) { return evaluate( ReduceOps.makeRef(identity, accumulator, accumulator)); }

Slide 47

Slide 47 text

ReduceOps.makeRef() java.util.stream.ReduceOps public static TerminalOp makeRef(U seed, BiFunction reducer, BinaryOperator combiner) { class ReducingSink extends Box implements AccumulatingSink { ...... } return new ReduceOp(StreamShape.REFERENCE) @Override public ReducingSink makeSink() { return new ReducingSink(); } };} 値を1つ持つコンテナ

Slide 48

Slide 48 text

終端操作関連のクラス構成 TerminalOp ReduceOp 終端操作の 遅延実行 Box ReduceSink 単一要素 コンテナ reduce処理

Slide 49

Slide 49 text

ReduceOps.makeRef() java.util.stream.ReduceOps public static TerminalOp makeRef(U seed, BiFunction reducer, BinaryOperator combiner) { class ReducingSink extends Box implements AccumulatingSink { ...... } return new ReduceOp(StreamShape.REFERENCE) @Override public ReducingSink makeSink() { return new ReducingSink(); } };}

Slide 50

Slide 50 text

ReferencePipeline.reduce() java.util.stream.ReferencePipeline @Override public final P_OUT reduce( final P_OUT identity, final BinaryOperator accumulator) { return evaluate( ReduceOps.makeRef(identity, accumulator, accumulator)); }

Slide 51

Slide 51 text

AbstractPipeline.evaluate() java.util.stream.AbstractPipeline final R evaluate(TerminalOp terminalOp) { assert getOutputShape() == terminalOp.inputShape(); if (linkedOrConsumed) throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; return isParallel() ? terminalOp.evaluateParallel( this, sourceSpliterator(terminalOp.getOpFlags())) : terminalOp.evaluateSequential( this, sourceSpliterator(terminalOp.getOpFlags())); }

Slide 52

Slide 52 text

ReduceOp.evaluateSequential() java.util.stream.ReduceOps @Override public R evaluateSequential(PipelineHelper helper, Spliterator spliterator) { return helper.wrapAndCopyInto(makeSink(), spliterator).get(); }

Slide 53

Slide 53 text

ReduceOps.makeRef() java.util.stream.ReduceOps public static TerminalOp makeRef(U seed, BiFunction reducer, BinaryOperator combiner) { class ReducingSink extends Box implements AccumulatingSink { ...... } return new ReduceOp(StreamShape.REFERENCE) @Override public ReducingSink makeSink() { return new ReducingSink(); } };}

Slide 54

Slide 54 text

ReduceOp.evaluateSequential() java.util.stream.ReduceOps @Override public R evaluateSequential(PipelineHelper helper, Spliterator spliterator) { return helper.wrapAndCopyInto(makeSink(), spliterator).get(); } ReducingSink 内部でStatelessOpの opWrapSinkをコール

Slide 55

Slide 55 text

ReferencePipeline.map() java.util.stream.ReferencePipeline public final Stream map( Function mapper) { Objects.requireNonNull(mapper); return new StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink opWrapSink(int flags, Sink sink) { return new Sink.ChainedReference<>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.apply(u)); } }; downstream

Slide 56

Slide 56 text

ReduceOp.evaluateSequential() java.util.stream.ReduceOps @Override public R evaluateSequential(PipelineHelper helper, Spliterator spliterator) { return helper.wrapAndCopyInto(makeSink(), spliterator).get(); } ReducingSink Sinkチェーンを生成

Slide 57

Slide 57 text

Head StatelessOp StatelessOp previousStage previousStage filter map ReducingSink downstream downstream reduce

Slide 58

Slide 58 text

ReduceOp.evaluateSequential() java.util.stream.ReduceOps @Override public R evaluateSequential(PipelineHelper helper, Spliterator spliterator) { return helper.wrapAndCopyInto(makeSink(), spliterator).get(); } ReducingSink Sinkチェーンを生成 copyIntoをコール

Slide 59

Slide 59 text

AbstractPipeline.copyInto() java.util.stream.AbstractPipeline @Override final void copyInto(Sink wrappedSink, Spliterator spliterator) { if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags() wrappedSink.begin(spliterator.getExactSizeIfKnown()); spliterator.forEachRemaining(wrappedSink); wrappedSink.end(); } else { copyIntoWithCancel(wrappedSink, spliterator); } } Sinkチェーンの先頭

Slide 60

Slide 60 text

IteratorSpliterator.forEachRemaining () java.util.Spliterators @Override public void forEachRemaining(Consumer action) { if (action == null) throw new NullPointerException(); Iterator i; if ((i = it) == null) { i = it = collection.iterator(); est = (long)collection.size(); } i.forEachRemaining(action); } Sinkチェーンの先頭 Iteratorを使用したイテレーション 内部でSink.accept()をコール Iterator取得

Slide 61

Slide 61 text

Head StatelessOp StatelessOp previousStage previousStage filter map ReducingSink downstream downstream reduce

Slide 62

Slide 62 text

ReduceOp.evaluateSequential() java.util.stream.ReduceOps @Override public R evaluateSequential(PipelineHelper helper, Spliterator spliterator) { return helper.wrapAndCopyInto(makeSink(), spliterator).get(); } ReducingSinkが保持する 結果を取り出す

Slide 63

Slide 63 text

終端操作 まとめ Sinkオブジェクトのチェーン生成 Sinkオブジェクトチェーンに対してイテレーション ReducingSinkが保持する結果を戻す

Slide 64

Slide 64 text

まとめ ラムダ式の遅延処理を活用 ラムダ式をキャプチャすることでコンテキストを保存 ラムダ式を引数、戻り値に使用する高階関数の多用 ラムダ式をいかに活用するかが重要 他にもラムダ式を活用しているAPIはいろいろ ex) Classfile API