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

On stream-lazy-computation

Embed Size (px)

Citation preview

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

@mike_neck

ことわりこの話を聞いてもアプリ作るのが爆速になるわけではありません

この話を聞いてもアプリが簡単に作れるようになるわけでもありません

この話を聞いても作るアプリケーションが安全になるわけでもありません

単なる知的好奇心を満たすかもしれませんが、儂が満足したいだけです

遅延評価の話はしませんよ

概要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パイプラインがやっていること

AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>とは?

Stream<P_OUT>の実装クラス

これをさらに継承したクラスにReferencePipeline<P_IN, P_OUT>という抽象クラスがある(ほぼこれが実体)

さらにStatelessOp<P_IN, P_OUT>、StatefullOp<P_IN, P_OUT>という抽象クラスがある

これらのパイプラインを構成するオブジェクトはデータ構造的に双方向リンクドリストとなって前後のオブジェクトをたどることができる

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の無名クラスの中に埋め込む形で処理を保持する

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<T>という抽象クラスで、リンクドリストの構造を持つ

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の合成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

・begin(long)は次の処理(Sink)のbegin(long)を呼び出す

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

・end()は次の処理(Sink)の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がリンクドリストになっていることで処理順を保持している

ChainedReference<?, A> implements

Sink<A>

Sink<?>

begin(long) begin(long)

accept(a) accept(?)

end() end()a -> b

ChainedReference<?, A> implements

Sink<A>

Sink<?>

begin(long) begin(long)

accept(a) accept(?)

end() end()a -> b

一番最後にあるSinkは何もの?

終端操作と処理開始

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

シグニチャーはTerminalSink<T, R> extends Sink<T>, Supplier<R>

終端操作と処理開始TerminalSink<T, R>を生成

パイプラインのリンクをたどりながら各パイプラインのSink opWrapSink(int, Sink)を呼び出してすべての処理を合成したSinkを取得する

合成Sinkのbeginを呼び出す

すべてのソース(Spliterator)にSinkを渡してacceptを呼び出させる

ソースが全てを消費あるいは、必要な結果が得られたら合成Sinkのendを呼び出す

終端操作と処理開始TerminalSink<T, R>を生成

パイプラインのリンクをたどりながら各パイプラインのSink opWrapSink(int, Sink)を呼び出してすべての処理を合成したSinkを取得する

合成Sinkのbeginを呼び出す

すべてのソース(Spliterator)にSinkを渡してacceptを呼び出させる

ソースが全てを消費あるいは、必要な結果が得られたら合成Sinkのendを呼び出す

終端操作呼び出しによってパイプラインをさかのぼって処理(Sink)を構築

SpliteratorにSinkを渡して処理を実行処理終了を処理に通知

まとめストリームパイプラインは双方向リンクドリストを形成するのみで、処理の実行は行わない

処理をリンクドリストで構築することによって処理順序を保証

終端操作でパイプラインを遡って処理を合成、ソースを順次適用

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

で検索🔎mike-neck Stream