社内java8勉強会 ラムダ式とストリームapi
DESCRIPTION
TRANSCRIPT
1 54
Java8基礎勉強会ラムダ式とストリームAPI
2014年3月25日
アリエルネットワーク 池添
2 54
本日のテーマ
3 54
for文を駆逐してやるこの世から1つ残らず
4 54
目次
bull 概要
bull ラムダ式の基礎
bull ストリームAPIの基礎
bull ストリームAPIの拡張
5 54
ラムダ式とストリームAPI
bull ラムダ式とは関数を簡便に表現するための記法
bull ストリームAPIはラムダ式を利用したコレクション操作用のAPI
bull 関数型プログラミング言語由来歴史は古い
bull これまでの手続き型やオブジェクト指向的なプログラミング手法から関数型プログラミングに変わります
bull パラダイムシフトのよかん
6 54
簡単なサンプル
bull フルーツの一覧の中からbull 名前がldquoりんごrdquoで始まりbull 値段が100円以上のものをbull 値段順で並び替えbull 名前だけを取り出してbull リストを作成する
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
7 54
メリット
bull 手続き的だった記述が宣言的になるbull 保守性の向上hellip
bull 可読性の向上hellip
bull 簡単に並列実行できるようになる
8 54
保守性が向上する
データの取得
データの取得
条件による抽出
条件による抽出
データの加工
データの加工
コンテナへの登録
コンテナへの登録
for文for文
データの初期化
データの取得
条件による抽出条件による抽出
データの加工コンテナへの
登録
ごちゃっ パイプライン的ですっきり
bull 処理の追加削除順番の入れ替えなどがやりやすい
bull 再利用性やテスタビリティも向上するかも
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
2 54
本日のテーマ
3 54
for文を駆逐してやるこの世から1つ残らず
4 54
目次
bull 概要
bull ラムダ式の基礎
bull ストリームAPIの基礎
bull ストリームAPIの拡張
5 54
ラムダ式とストリームAPI
bull ラムダ式とは関数を簡便に表現するための記法
bull ストリームAPIはラムダ式を利用したコレクション操作用のAPI
bull 関数型プログラミング言語由来歴史は古い
bull これまでの手続き型やオブジェクト指向的なプログラミング手法から関数型プログラミングに変わります
bull パラダイムシフトのよかん
6 54
簡単なサンプル
bull フルーツの一覧の中からbull 名前がldquoりんごrdquoで始まりbull 値段が100円以上のものをbull 値段順で並び替えbull 名前だけを取り出してbull リストを作成する
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
7 54
メリット
bull 手続き的だった記述が宣言的になるbull 保守性の向上hellip
bull 可読性の向上hellip
bull 簡単に並列実行できるようになる
8 54
保守性が向上する
データの取得
データの取得
条件による抽出
条件による抽出
データの加工
データの加工
コンテナへの登録
コンテナへの登録
for文for文
データの初期化
データの取得
条件による抽出条件による抽出
データの加工コンテナへの
登録
ごちゃっ パイプライン的ですっきり
bull 処理の追加削除順番の入れ替えなどがやりやすい
bull 再利用性やテスタビリティも向上するかも
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
3 54
for文を駆逐してやるこの世から1つ残らず
4 54
目次
bull 概要
bull ラムダ式の基礎
bull ストリームAPIの基礎
bull ストリームAPIの拡張
5 54
ラムダ式とストリームAPI
bull ラムダ式とは関数を簡便に表現するための記法
bull ストリームAPIはラムダ式を利用したコレクション操作用のAPI
bull 関数型プログラミング言語由来歴史は古い
bull これまでの手続き型やオブジェクト指向的なプログラミング手法から関数型プログラミングに変わります
bull パラダイムシフトのよかん
6 54
簡単なサンプル
bull フルーツの一覧の中からbull 名前がldquoりんごrdquoで始まりbull 値段が100円以上のものをbull 値段順で並び替えbull 名前だけを取り出してbull リストを作成する
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
7 54
メリット
bull 手続き的だった記述が宣言的になるbull 保守性の向上hellip
bull 可読性の向上hellip
bull 簡単に並列実行できるようになる
8 54
保守性が向上する
データの取得
データの取得
条件による抽出
条件による抽出
データの加工
データの加工
コンテナへの登録
コンテナへの登録
for文for文
データの初期化
データの取得
条件による抽出条件による抽出
データの加工コンテナへの
登録
ごちゃっ パイプライン的ですっきり
bull 処理の追加削除順番の入れ替えなどがやりやすい
bull 再利用性やテスタビリティも向上するかも
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
4 54
目次
bull 概要
bull ラムダ式の基礎
bull ストリームAPIの基礎
bull ストリームAPIの拡張
5 54
ラムダ式とストリームAPI
bull ラムダ式とは関数を簡便に表現するための記法
bull ストリームAPIはラムダ式を利用したコレクション操作用のAPI
bull 関数型プログラミング言語由来歴史は古い
bull これまでの手続き型やオブジェクト指向的なプログラミング手法から関数型プログラミングに変わります
bull パラダイムシフトのよかん
6 54
簡単なサンプル
bull フルーツの一覧の中からbull 名前がldquoりんごrdquoで始まりbull 値段が100円以上のものをbull 値段順で並び替えbull 名前だけを取り出してbull リストを作成する
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
7 54
メリット
bull 手続き的だった記述が宣言的になるbull 保守性の向上hellip
bull 可読性の向上hellip
bull 簡単に並列実行できるようになる
8 54
保守性が向上する
データの取得
データの取得
条件による抽出
条件による抽出
データの加工
データの加工
コンテナへの登録
コンテナへの登録
for文for文
データの初期化
データの取得
条件による抽出条件による抽出
データの加工コンテナへの
登録
ごちゃっ パイプライン的ですっきり
bull 処理の追加削除順番の入れ替えなどがやりやすい
bull 再利用性やテスタビリティも向上するかも
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
5 54
ラムダ式とストリームAPI
bull ラムダ式とは関数を簡便に表現するための記法
bull ストリームAPIはラムダ式を利用したコレクション操作用のAPI
bull 関数型プログラミング言語由来歴史は古い
bull これまでの手続き型やオブジェクト指向的なプログラミング手法から関数型プログラミングに変わります
bull パラダイムシフトのよかん
6 54
簡単なサンプル
bull フルーツの一覧の中からbull 名前がldquoりんごrdquoで始まりbull 値段が100円以上のものをbull 値段順で並び替えbull 名前だけを取り出してbull リストを作成する
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
7 54
メリット
bull 手続き的だった記述が宣言的になるbull 保守性の向上hellip
bull 可読性の向上hellip
bull 簡単に並列実行できるようになる
8 54
保守性が向上する
データの取得
データの取得
条件による抽出
条件による抽出
データの加工
データの加工
コンテナへの登録
コンテナへの登録
for文for文
データの初期化
データの取得
条件による抽出条件による抽出
データの加工コンテナへの
登録
ごちゃっ パイプライン的ですっきり
bull 処理の追加削除順番の入れ替えなどがやりやすい
bull 再利用性やテスタビリティも向上するかも
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
6 54
簡単なサンプル
bull フルーツの一覧の中からbull 名前がldquoりんごrdquoで始まりbull 値段が100円以上のものをbull 値段順で並び替えbull 名前だけを取り出してbull リストを作成する
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
7 54
メリット
bull 手続き的だった記述が宣言的になるbull 保守性の向上hellip
bull 可読性の向上hellip
bull 簡単に並列実行できるようになる
8 54
保守性が向上する
データの取得
データの取得
条件による抽出
条件による抽出
データの加工
データの加工
コンテナへの登録
コンテナへの登録
for文for文
データの初期化
データの取得
条件による抽出条件による抽出
データの加工コンテナへの
登録
ごちゃっ パイプライン的ですっきり
bull 処理の追加削除順番の入れ替えなどがやりやすい
bull 再利用性やテスタビリティも向上するかも
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
7 54
メリット
bull 手続き的だった記述が宣言的になるbull 保守性の向上hellip
bull 可読性の向上hellip
bull 簡単に並列実行できるようになる
8 54
保守性が向上する
データの取得
データの取得
条件による抽出
条件による抽出
データの加工
データの加工
コンテナへの登録
コンテナへの登録
for文for文
データの初期化
データの取得
条件による抽出条件による抽出
データの加工コンテナへの
登録
ごちゃっ パイプライン的ですっきり
bull 処理の追加削除順番の入れ替えなどがやりやすい
bull 再利用性やテスタビリティも向上するかも
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
8 54
保守性が向上する
データの取得
データの取得
条件による抽出
条件による抽出
データの加工
データの加工
コンテナへの登録
コンテナへの登録
for文for文
データの初期化
データの取得
条件による抽出条件による抽出
データの加工コンテナへの
登録
ごちゃっ パイプライン的ですっきり
bull 処理の追加削除順番の入れ替えなどがやりやすい
bull 再利用性やテスタビリティも向上するかも
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
9 54
可読性は
bull 野球選手の一覧からチームごとの投手の平均年俸を取得する処理の例
1 MapltString Doublegt m = playersstream()
2 collect(CollectorsgroupingBy(player -gt playergetTeam()))
3 entrySet()
4 stream()
5 collect(CollectorstoMap(
6 entry -gt entrygetKey()
7 entry -gt entrygetValue()stream()
8 filter(player -gt playergetPosition()equals(投手))
9 mapToInt(player -gt playergetSalary())
10 average()
11 orElse(0)
12 )
13 )
bull 気をつけないとすぐに読みにくくなる
bull stream()とかstream()とかstream()とかノイズが多い
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
10 54
ラムダ式の基礎
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
11 54
むかしばなし
Javaでdelegate型を扱えるようにしたでJ++って言いますねん
互換性のないものはJavaとは呼べません提訴します
なんとまあセンスの悪い言語設計でしょうまともな言語設計者ならポリシーが
許さないと思います
じゃあ自分らで言語つくりますわCって言うやつ
1997年
1998年
2000年
delegateをラムダ式で書けるようにしたで 2005年
Microsoft
Sun
某研究者
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
12 54
なぜラムダ式が必要になったのか
bull 非同期処理や並列処理が当たり前に使われるようになりラムダ式の必要性が高まった
bull Microsoftの提案を受け入れていればラムダ式がもっと早く入っていたかもしれない
bull でも15年も先を見越して言語設計するなんてことは難しい
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
13 54
ラムダ式
bull 関数を第一級オブジェクトとして扱えるようになった
bull JVMで関数を直接扱えるようになったわけではなく内部的にはクラスのインスタンスを使って表現している
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
14 54
ラムダ式の書き方
() -gt 123
x -gt x x
(x y) -gt x + y
(int x int y) -gt
return x + y
いろいろ省略できる仮引数や戻り値の型はほとんど型推論してくれるかしこい
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
15 54
ラムダ式の使い方
bull ラムダ式を渡される側
void hoge(FunctionltInteger Integergt func)
Integer ret = funcapply(42)
bull ラムダ式を渡す側
hoge(x -gt x x)
関数型インタフェース
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
16 54
関数型インタフェース
bull ラムダ式の型は関数型インタフェースで表現される
bull 関数型インタフェースとはbull 実装するべきメソッドを1つだけ持ってるinterface
bull FunctionalInterfaceアノテーションを付けるとコンパイル時にメソッドが1つだけかどうかチェックしてくれるつけなくてもよい
FunctionalInterfacepublic interface FunctionltT Rgt
R apply(T t)
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
17 54
標準の関数型インタフェース
bull Runnable Consumer Function PredicateSupplier
bull BiConsumerBiFunctionBiPredicate
bull BooleanSupplier
bull IntBinaryOperatorIntConsumerIntFunctionIntPredicateIntSupplierIntToDoubleFunctionIntToLongFunctionIntUnaryOperatorObjIntConsumerToIntBiFunctionToIntFunction
bull LongBinaryOperatorLongConsumerLongFunctionLongPredicateLongSupplierLongToDoubleFunctionLongToIntFunctionLongUnaryOperatorObjLongConsumerToLongBiFunctionToLongFunction
bull DoubleBinaryOperatorDoubleConsumerDoubleFunctionDoublePredicateDoubleSupplierDoubleToIntFunctionDoubleToLongFunctionDoubleUnaryOperatorObjDoubleConsumerToDoubleBiFunctionToDoubleFunction
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
18 54
代表的な関数型インタフェース
bull Runnable 引数なし戻り値なし
bull Consumer 引数1つ戻り値なし
bull Function 引数1つ戻り値あり
bull Predicate 引数1つ戻り値がboolean
bull Supplier 引数なし戻り値あり
bull Bi + Xxxxx 引数が2つ
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
19 54
無名内部クラスとラムダ式の違い
無名内部クラス ラムダ式
外部変数へのアクセス 実質的なfinalの変数のみアクセス可能
実質的なfinalの変数のみアクセス可能
エンクロージングインスタンスの参照
必ず持っている 必要がなければ持たないメモリリークがおきにくい
クラスファイルの生成 コンパイル時にクラスが生成される
実行時にクラスが生成される
クラスのロード時間が短縮されるかも
インスタンスの生成 明示的にnewする JVMが最適な生成方法を選択する
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
20 54
Javaのラムダ式はクロージャではない
bull ローカル変数は実質的にfinalな変数にしかアクセスできない
bull 独自のスコープは持たず外のスコープを引き継ぐ
bull エンクロージングインスタンスの参照は基本持たない
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
21 54
ラムダ式のスコープ
class Outer public void func() final int a = 0int b = 1liststream()forEach(x -gt int a = 2Systemoutprintln(b)
)b = 3
値の変更が行われるローカル変数はアクセス不可
独自のスコープを持たないので変数名の衝突が起きる
明示しなければエンクロージングインスタンスの参照を持たない
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
22 54
例外は苦手かも
bull ラムダ式からチェック例外のあるAPIを呼び出す場合bull 関数型インタフェースの定義にthrowsを記述する(標準の関数型インタフェースにはついてない)
bull ラムダ式の中でtry-catchを書く
listmap(x -gt try チェック例外のある呼び出し
catch(XxxException ex) エラー処理
)collect(CollectorstoList())
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
23 54
メソッド参照
bull ラムダ式だけでなく既存のメソッドも関数型インタフェースで受け取ることが可能
ラムダ式を使った場合
listforEach(x -gt Systemoutprintln(x))
メソッド参照を使った場合
listforEach(Systemoutprintln)
fruitsmap(fruit -gt fruitgetName())
インスタンスメソッドの参照もOK
fruitsmap(FruitgetName)
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
24 54
Lambda Expression Deep Dive
bull ラムダ式は無名内部クラスのシンタックスシュガーじゃないbull コンパイル時ではなく実行時にクラスが生成されるbull invokeDynamic命令を使っている
bull ラムダ式をコンパイルするとどんなバイトコードが生成されるかみてみよう
対象のコードはこんな感じ
public class Main
public void main()
Sample sample = new Sample()
samplefunc(x -gt x x)
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
25 54
ラムダ式のバイトコード
INVOKEDYNAMIC apply()LjavautilfunctionIntFunction [
handle kind 0x6 INVOKESTATIC
javalanginvokeLambdaMetafactorymetafactory()
arguments
(I)LjavalangObjectclass
handle kind 0x6 INVOKESTATIC
Mainlambda$main$0((I)LjavalangInteger)
(I)LjavalangIntegerclass
]
BIPUSH 12
INVOKEVIRTUAL Samplefunc (LjavautilfunctionIntFunctionI)I
途中省略
private static lambda$main$0(I)LjavalangInteger
L0
ラムダ式の中の処理 x -gt x x
ラムダのオブジェクトをスタックに積んでメソッドを呼び出す
ラムダ式の
オブジェクトをつくる命令
ラムダ式のなかみ
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
26 54
ラムダ式の実行時の挙動
MainclassMainclass
コンパイル時に生成されるもの
invoke dynamicinvoke dynamic
static methodlambda$main$0static methodlambda$main$0
LambdaMetafactorymetafactory
LambdaMetafactorymetafactory
ラムダのインスタンスをつくる
メソッド
ラムダのインスタンスをつくる
メソッド
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
class Lambda$1
関数型インタフェースを実装
lambda$main$0を呼び出す
JVMの中のクラス
実行時に作られるもの
1回目の呼び出し(ブートストラップ)
2回目以降の呼び出し(Method Handle)
作成 Mainの内部クラスとして作成
new
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
27 54
なぜこんな複雑なことを
bull コンパイル時にクラスを大量につくるとクラスロードのときに遅くなるかもしれないからとくにストリームAPIではラムダ式を大量につかうので
bull invokeDynamicを使うとパフォーマンスをあまり落とさず動的な処理が実現できる
bull 今後JVMの実装が変わるとインスタンスの生成方法がより効率的なものになるかも
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
28 54
ストリームAPIの基礎
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
29 54
ストリームAPIとは
bull パイプライン型のデータ処理のAPI
bull 絞り込みデータ変換グループ化集計などの操作をそれぞれ分離した形で記述できる
bull 絞り込みの条件や加工方法などをラムダ式で指定する
bull メソッドチェイン形式で記述できる
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
30 54
ストリームパイプライン
bull ストリームパイプラインの構成要素bull ソース(Source)
bull 中間操作(Intermediate Operation)
bull 終端操作(Terminal Operation)
bull ストリームパイプラインは1つ以上のソース0個以上の中間操作1つの終端操作から構成される
bull 1つのStreamに対して複数の終端操作(いわゆる分配)をおこなってはいけない
bull 終端操作の結果をソースとして処理を継続することも可能
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
31 54
サンプル
bull 1行目がソース
bull 2行目から5行目までが中間操作
bull 6行目が終端操作
1 ListltStringgt apples = fruitsstream()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
32 54
ストリームパイプラインの挙動
中間
操作
中間
操作
中間
操作
結果
filterの条件に
一致しなければ以降の処理は実行しない
ソースの要素を1つずつ中間操作に流す
終端操作を呼び出したときに初めて全体の処理が動く
それまで中間操作は実行されない
ソース
終端操作を実行
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
33 54
ソース
bull 既存のデータからStream型のオブジェクトをつくる
bull Streamの種類bull StreamltTgt
bull IntStream LongStream DoubleStream
bull つくりかたbull Collectionstream
bull Arraysstream
bull Streamof
bull BufferReaderlines
bull IntStreamrange
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
34 54
ソースの特性
bull Sequential Parallelbull 逐次実行か並列実行か
bull StreamparallelとStreamsequentialで相互に変換可能
bull Ordered Unorderedbull Listや配列などはOrdered SetなどはUnordered
bull Streamunorderedで順序を保証しないStreamに変換可能
bull 無限リスト
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
35 54
中間操作
bull 絞り込みや写像などの操作を指定して新しいStreamを返す
bull 遅延実行bull 処理方法を指定するだけで実際には処理しない
bull 中間操作を呼び出すたびにループしてたら効率が悪い終端操作が呼ばれたときに複数の中間操作をまとめてループ処理
bull 処理の種類bull 絞り込み filterbull 写像 map flatMapbull 並び替え sortedbull 数の制御 limit skipbull 同一要素除外 distinctbull tee的なもの peek
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
36 54
特殊な中間操作
bull ステートフルな中間操作distinct sortedbull 中間操作は基本的にステートレスだがステートフルなものもある
bull 無限リストや並列処理のときに注意が必要
bull ショートサーキット評価な中間操作limitbull 指定された数の要素を流したら処理を打ち切る
bull 無限Streamを有限Streamにしてくれる
bull 副作用向け中間操作peekbull 中間操作では基本的に副作用するべきでない
bull デバッグやログ出力用途以外にはなるべく使わないようにしよう
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
37 54
終端操作
bull ストリームパイプラインを実行してなんらかの結果を取得する処理forEachだけは戻り値を返さない副作用専用のメソッド
bull 処理の種類bull たたみ込みcollect reduce
bull 集計min max average sum count
bull 単一の値の取得findFirst findAny
bull 条件allMatch anyMatch noneMatch
bull 繰り返しforEach forEachOrdered
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
38 54
汎用的な終端操作collect
bull 引数にCollectorを指定して様々な処理がおこなえる
bull 集計bull averagingInt averagingLong averagingDoublebull summingInt summingLong summingDoublebull countingbull maxBy minBybull summarizing
bull グループ化bull groupingBy partitioningBy
bull コンテナに累積bull toList toMap toSet
bull 結合bull joining
bull たたみ込みbull reducing
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
39 54
Optionalを返す終端操作
bull Optional
bull nullチェックめんどくさいパイプラインの途中でnullが現れると流れが止まってしまう
bull nullを駆逐し(ry
bull Java8ではOptional型が入った
bull Stream APIの中にはOptionalを返す終端操作があるbull min max average sum findFirst findAny reduceなど
bull 空のリストに対してこれらの処理を呼び出すとOptionalemptyを返す
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
40 54
ショートサーキット評価の終端操作
bull ショートサーキット評価をする終端操作bull anyMatch allMatch noneMatch findFirst findAny
bull 例えばStreamcountは全要素をループで回すので時間がかかるがStreamfindAnyはショートサーキット評価なので1つめの要素が見つかればすぐに終わる
if(streamcount() = 0)
if(streamfindAny()isPresent())
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
41 54
並列処理
bull すごく簡単に並列化できるソースを生成するときにCollectionparallelStreamや Streamparallelを使うだけ
bull 順序保証bull ソースがORDEREDであれば並列実行しても順番は保証される
bull ただし順序を保つために内部にバッファリングするのであまりパフォーマンスはよくない
bull 順番を気にしないのであればunorderedすると効率がよくなる
bull 副作用に注意bull 中間操作で副作用が生じる場合はちゃんとロックしよう
bull ただしロックすると並列で実行しても待ちが多くなるのでパフォーマンスはよくない
bull スレッドセーフでないArrayListなども並列実行可能
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
42 54
並列処理のやりかた
bull parallel()をつけるだけ
bull ただし上記のような例ではあまり並列処理のうまみはない
1 ListltStringgt apples = fruitsstream()parallel()
2 filter(f -gt fgetName()startsWith(りんご)) 3 filter(f -gt fgetPrice() gt 100)
4 sorted(ComparatorcomparingInt(FruitgetPrice))
5 map(FruitgetName)
6 collect(CollectorstoList())
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
43 54
並列処理の挙動
中間
操作
中間
操作
中間
操作
結果 43
分割したソースごとに異なるスレッドで中間操作を並列に実行
ソースを複数に分割spliterator
順序を保証する場合は内部でバッファリング
中間
操作
中間
操作
中間
操作
複数スレッドの実行結果を結合終端操作
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
44 54
ストリームAPIの拡張
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
45 54
ストリームAPIは機能不足
bull なんかいろいろ足りないbull 他の言語と比べて標準で用意されてる機能が少ない
bull 複数のStreamを合成するzipすらないなんてhellip
bull 毎回stream()を呼ぶのめんどくさいhellip
bull 足りないならつくればいいじゃないbull 関数型言語なら関数をどんどん増やせばよいCなら拡張メソッドという仕組みがある
bull でもJavaではStreamに自由にメソッドを増やせないこまった
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
46 54
ストリームAPIの拡張ポイント
bull たたみ込みを使えばたいていの処理はつくれる
bull 汎用的なCollectorをつくって再利用できるようにしておくとよさそう
bull CollectorをつくるためのヘルパーAPIが用意されている既存のCollectorを組み合わせたり一から新しい終端操作をつくったりすることができる
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
47 54
Collectorの構成要素
bull supplierbull コンテナの初期値を生成する人
bull accumulatorbull 値を加工してコンテナに格納する人
bull combinerbull 並列で実行された場合コンテナを結合する人
bull finisherbull 最後にコンテナを加工する人
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
48 54
Collectorの動き
コンテナコンテナ コンテナコンテナ
コンテナコンテナ結果結果
supplier supplier
combiner
finisher
accumulator accumulator
並列的にデータがやってくる
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
49 54
Collectorのつくり方
bull Collectorofbull supplier accumulator combiner finisherを指定して新しいCollectorをつくる
bull CollectorsmapMergerbull Collectorにcombinerを追加する
bull Collectorsmappingbull accumulatorの前に実行される写像処理を追加する
bull CollectorscollectingAndThenbull Collectorにfinisherを追加する
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
50 54
Collectorを使った例
bull チームごとの投手の平均年俸を取得する
bull 2つのCollectorを用意bull グループ化したストリームを返すCollector
bull グループごとの平均値を返すCollector
1 MapltString Doublegt averageSalaryMap = playersstream()
2 filter(player -gt playergetPosition()equals(投手))
3 collect(groupedStreamCollector(PlayergetTeam))
4 collect(averagePerGroupCollector(PlayergetSalary))
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
51 54
Collectorの実装1 static ltT Vgt CollectorltEntryltV ListltTgtgt MapltV Doublegtgt
2 averagePerGroupCollector(ToIntFunctionltTgt mapper)
3 return Collectorof(
4 () -gt new HashMapltgt()
5 (map entry) -gt
6 entrygetValue()stream()
7 mapToInt(mapper)
8 average()
9 ifPresent(ave -gt mapput(entrygetKey() ave))
10
11 (left right) -gt
12 leftputAll(right)
13 return left
14
15 )
16
17 static ltT Kgt CollectorltT StreamltEntryltK ListltTgtgtgtgt
18 groupedStreamCollector(FunctionltT extends Kgt mapper)
19 return CollectorscollectingAndThen(
20 CollectorsgroupingBy(mapper) map -gt mapentrySet()stream())
21
accumulator
combiner
finisher
bull つくるのは難しいけど汎用化しておけばいろいろなところで使える
supplier
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
52 54
まとめ
bull ラムダ式bull 関数を簡便に表記するための記法
bull デメリットも少ないし使わない手はないよね
bull ストリームAPIbull 慣れるまでは読み書きも難しいし変なバグを生みやすいかもしれない
bull でも慣れると少ない記述で複雑な処理が実現できるそして何より書いていて楽しい
bull しかし標準で用意されてる機能が少ないのに拡張が難しいのはどうにかならんかね
bull 今後はオレオレコレクションやStreamUtilsクラスが乱立する可能性も
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
53 54
おまけ
bull Java8を使うときはIntelliJ IDEAがおすすめ(EclipseのJava8正式対応は2014年6月)
bull Java8の文法にもしっかり対応(たまーに型推論間違えたりするけど)
bull 無名内部クラスを書いてるとラムダ式に書き換えてくれる
bull for文を書いてるとストリームAPIに書き換えてくれる(複雑な処理はムリだけど)
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda
54 54
参考
bull JavaのLambdaの裏事情bull httpwwwslidesharenetnowokayjava-2898601
bull ラムダと invokedynamic の蜜月bull httpwwwslidesharenetmiyakawatakulambda-meetsinvokedynamic
bull 倭マンs BLOGbull httpwamanhatenablogcomcategoryJava8
bull ラムダ禁止について本気出して考えてみた - 9つのパターンで見るStream API
bull httpacro-engineerhatenablogcomentry20131216235900
bull Collectorを征す者はStream APIを征す(部分的に)bull httpblogexoegonet201312control-collector-to-rule-stream-
apihtml
bull きしだのはてなbull httpdhatenanejpnowokaysearchdiaryword=2A5Bjava85D
bull 徹底解説Project Lambdaのすべて returnsbull httpwwwslidesharenetbitter_foxjava8-launchJava
bull SE 8 lambdaで変わるプログラミングスタイルbull httpptslidesharenetnowokaylambdajava-se-8-lambda