社内java8勉強会 ラムダ式とストリームapi

54
1 / 54 Java8基礎勉強会 ラムダ式とストリームAPI 2014325アリエル・ネットワーク 池添

Upload: akihiro-ikezoe

Post on 18-Dec-2014

26.702 views

Category:

Documents


3 download

DESCRIPTION

 

TRANSCRIPT

Page 1: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 2: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 3: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 4: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 5: 社内Java8勉強会 ラムダ式とストリーム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

Page 6: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 7: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 8: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 9: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 10: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 11: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 12: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 13: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 14: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 15: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 16: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 17: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 18: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 19: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 20: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 21: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 22: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 23: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 24: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 25: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 26: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 27: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 28: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 29: 社内Java8勉強会 ラムダ式とストリーム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

Page 30: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 31: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 32: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 33: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 34: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 35: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 36: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 37: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 38: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 39: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 40: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 41: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 42: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 43: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 44: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 45: 社内Java8勉強会 ラムダ式とストリーム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

Page 46: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 47: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 48: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 49: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 50: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 51: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 52: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 53: 社内Java8勉強会 ラムダ式とストリームAPI

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

Page 54: 社内Java8勉強会 ラムダ式とストリーム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