JavaのStreamで学ぶ遅延処理実装パターン

36
JavaStreamで学ぶ 遅延処理実装パターン @mike_neck

Transcript of JavaのStreamで学ぶ遅延処理実装パターン

JavaのStreamで学ぶ遅延処理実装パターン

@mike_neck

ことわり

渋谷Javaの日程を間違えて、先週に資料上げてるから、内容を皆さんもうご存知なので、雑にやります

概要Streamの復習

Streamについての疑問

Streamパイプラインがやっていること

Sinkの合成

終端操作と処理開始

誰?持田真哉(@mike_neck)

たんなるJava、Groovy好きのおっさん

某IDEを売っている会社に入り浸って、クソコラを作ってる

2015年期待している陸上選手

青山聖佳(400m)、水口怜(走幅跳)、藤光謙司(200m)、高瀬慧(100m)

Streamの復習for文を簡単に今っぽく書くやつ

InputStreamと関係ない(全く関係ないわけでもないけど)

ScalaのStreamとは違う

順次および並列での集約操作をサポートする要素のシーケンス(Javadocより)

Streamの復習repo.findAllUsers().stream()

.filter(User::isMen)

.filter(u -> u.getAge() >= 27)

.filter(u -> u.getAge() <= 35)

.map(User::getHobbies)

.flatMap(List::stream)

.collect(groupingBy(

Hobby::getGenre,

counting()));

ソース

ストリームパイプライン

終端操作

Streamの疑問repo.findAllUsers().stream()

.filter(User::isMen)

.filter(u -> u.getAge() >= 27)

.filter(u -> u.getAge() <= 35)

.map(User::getHobbies)

.flatMap(List::stream)

.collect(groupingBy(

Hobby::getGenre,

counting()));

・これらの処理はどのように保持されるの?・処理の順番はどのように保持されるの?・終端操作呼び出しでどのように処理が実行されるの?

Streamパイプラインがやっていること

repo.findAllUsers().stream()

.filter(User::isMen)

.filter(u -> u.getAge() >= 27)

.filter(u -> u.getAge() <= 35)

.map(User::getHobbies)

.flatMap(List::stream)

.collect(groupingBy(

Hobby::getGenre,

counting()));

・AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>のインスタンスを生成して返す

Streamパイプラインがやっていること

.filter(User::isMen) AbstractPipelineUser:isMen

Streamパイプラインがやっていること

.filter(User::isMen)

.map(User::getHobbies)

AbstractPipeline

AbstractPipeline

User:isMen

User::getHobbies

Streamパイプラインがやっていること

.filter(User::isMen)

.map(User::getHobbies)

.flatMap(List::stream)

AbstractPipeline

AbstractPipeline

AbstractPipeline

User:isMen

User::getHobbies

List::stream

Streamパイプラインがやっていること

.filter(User::isMen)

.map(User::getHobbies)

.flatMap(List::stream)

AbstractPipeline

AbstractPipeline

AbstractPipeline

User:isMen

User::getHobbies

List::stream

AbstractPipelineの逆順リストを作っている

public final <R> Stream map(

Function<? super P_OUT, ? extends R> mapper) {

return

new StatelessOp<P_OUT, R>(this, …) {

@Override Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {

return new Sink.ChainedReference<P_OUT, R>(sink) {

public void accept(P_OUT u) {downSink.accept(mapper.apply(u));}

}

Streamパイプラインがやっていること

返されるStreamの無名クラスの中のopWrapSinkメソッドに埋め込まれる

public final <R> Stream map(

Function<? super P_OUT, ? extends R> mapper) {

return

new StatelessOp<P_OUT, R>(this, …) {

@Override Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {

return new Sink.ChainedReference<P_OUT, R>(sink) {

public void accept(P_OUT u) {downSink.accept(mapper.apply(u));}

}

public final <R> Stream map(

Function<? super P_OUT, ? extends R> mapper) {

return

new StatelessOp<P_OUT, R>(this, …) {

@Override Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {

return new Sink.ChainedReference<P_OUT, R>(sink) {

public void accept(P_OUT u) {downSink.accept(mapper.apply(u));}

}

Sinkの合成

Sinkの合成Sink<T>とは

個々の処理実行・制御を提供するConsumer<T>の拡張インターフェース

初期状態 ! begin(long) ! 処理中 ! accept(T) ! 処理中 ! end() ! 初期状態

後方へのリンクを持つリスト

Sinkの合成ChainedReference<B, A> implements

Sink<A>

ChainedReference<C, B> implements

Sink<B>

begin(long) begin(long)

accept(a) accept(b)

end() end()a -> b b -> c

・先に行われる処理(Sink)は次に行われる処理(Sink)へのリン

クを持っている

Sinkの合成ChainedReference<B, A> implements

Sink<A>

ChainedReference<C, B> implements

Sink<B>

begin(long) begin(long)

accept(a) accept(b)

end() end()a -> b b -> c

・accept(A)は処理を施した後に次の処理(Sink)のaccept(B)を呼び出す

Sinkの合成ChainedReference<B, A> implements

Sink<A>

ChainedReference<C, B> implements

Sink<B>

begin(long) begin(long)

accept(a) accept(b)

end() end()a -> b b -> c

Sinkがリンクドリストになっていることで処理順を保持している

仕組みはわかったけど、Sinkの合成はいつやるん?

終端操作と処理開始

一番最後にあるSinkは終端操作TerminalOp<T, R>にて生成されるTerminalSink<T, R>

終端操作はAbstractPipelineの逆順リストをたどってSinkを合成していく

終端操作と処理開始AbstractPipeline

AbstractPipeline

AbstractPipeline

Sink

Sink

Sink

TerminalOpTerminalSink

終端操作と処理開始AbstractPipeline

AbstractPipeline

AbstractPipeline

Sink

Sink

Sink

TerminalOpTerminalSink

終端操作と処理開始AbstractPipeline

AbstractPipeline

AbstractPipeline

Sink

Sink

Sink

TerminalOpTerminalSink

終端操作と処理開始AbstractPipeline

AbstractPipeline

AbstractPipeline

Sink

Sink

Sink

TerminalOpTerminalSink

終端操作と処理開始AbstractPipeline

AbstractPipeline

AbstractPipeline

Sink

Sink

Sink

TerminalOpTerminalSink

終端操作と処理開始AbstractPipeline

AbstractPipeline

AbstractPipeline

Sink

Sink

Sink

TerminalOpTerminalSink

ElementElementElementElement

終端操作と処理開始AbstractPipeline

AbstractPipeline

AbstractPipeline

Sink

Sink

Sink

TerminalOpTerminalSink

ElementElementElementElement

終端操作と処理開始AbstractPipeline

AbstractPipeline

AbstractPipeline

Sink

Sink

Sink

TerminalOpTerminalSink

ElementElementElementElement

まとめストリームパイプラインはStreamを実装した無名クラスのインスタンスの逆順リストを作る

StreamのopWrapSinkに各処理(Function,Predicate)が埋め込まれる

処理順リストによって処理順序を保証

終端操作で逆順Streamインスタンスを遡って処理を合成、ソースを順次適用

以下、応用

• Java8の目玉機能といえば…

Optionalですね

Optionalの操作順序

• Javaらしく正格に実行されます

今のStreamの実装パターンをもってLazyなOptional作れな

いか?

というわけで作っときました

• 無駄にLazyなOptional

• https://gist.github.com/mike-neck/ebb6e1b170f8db8250f8

おわり(最後は雑にまとめた)

で検索🔎mike-neck Stream