94
きつねさんと学ぶ Lambda&StreamAPIハンズオン 2015/7/11 @ 関ジャバ

きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Embed Size (px)

Citation preview

Page 1: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

きつねさんと学ぶLambda式&StreamAPIハンズオン

2015/7/11 @ 関ジャバ

Page 2: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

2

Who are you?

吉田真也(@bitter_fox, @shinyafox)

● 立命館大学 情報理工学部 情報システム学科– 高性能計算機ソフトウェアシステム研究室

● 立命館コンピュータクラブ(RCC)● http://www.rcc.ritsumei.ac.jp/

● OpenJDKコミッタ– Project Kulla

Page 3: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

3

Project Kulla

Page 4: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

ハンズオンの諸注意

Page 5: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

1.わからないことがあったらTAさんを呼んでください

Page 6: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava
Page 7: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

2.IDEの自動変換機能禁止

Page 8: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

3.本ハンズオンではメソッド参照は使用しません

Page 9: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main1

ラムダ式導入

匿名クラスで書かれたコードをラムダ式で置き換えてみましょう

Page 10: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

ラムダ式

● 関数型インターフェースを実装&インスタンス化する– メソッドが一つのインターフェース– Runnable, Function<T>, etc...

● (引数) -> {文}● (引数) -> 式

Page 11: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main2

関数型インターフェース

自分で関数型インターフェースを定義してみよう

Page 12: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

関数型インターフェース

● メソッドが一つのインターフェース

interface F {

void exec0();

}

● @FunctionalInterfaceアノテーション– コンパイル時に関数型インターフェースかを検査

Page 13: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Any question?

Page 14: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main3

内部イテレーションへ

(拡張)for文をIterable#forEachで書き換えよう

Page 15: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main3

List<Integer>の要素を

一行ずつ出力

for (Integer n : list) {

System.out.println(n);

}

Page 16: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

外部イテレーション?内部イテレーション?

● 外部イテレーション– イテレーションをユーザコードが行う– for文,while文

– 並列化が困難● ユーザコードの大幅な変更が必要

● 内部イテレーション– イテレーションをライブラリが行う– イテレーション以外の処理を受け取る– 並列化が容易

● イテレーションの方法を簡単に切り替えられる● ユーザコードの変更は最小限

Page 17: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Iterable#forEach

list.forEach(n ->

System.out.println(n));

Page 18: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main4

StreamAPI導入

複雑なイテレーションをStreamAPIで書いてみよう

Page 19: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main4

List<Integer>の

2で割り切れる

要素だけを一行ずつ出力

Page 20: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

StreamAPI

● 内部イテレーション&パイプライン化● 汎用性高い

– あらゆる形式のデータ列に対応可● Collection● 値の範囲● 任意の値

– データ列自体は保持していない● データへのアクセス手段を保持

Page 21: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

21

StreamAPI

java.util.stream.

Stream<T>

IntStream

LongStream

DoubleStream– ソースから生成される– 中間操作と終端操作でデータを弄る– 並列化が容易

Page 22: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

22

Collection

配列

BufferReader

etc...

Stream

IntStream

LongStream

DoubleStream

中間操作

終端操作

j.u.stream.*Source

Page 23: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

23

中間操作?

● Stream#filterやmapなど

– Streamを返すメソッド

● 終端操作が行われるまで処理されない– 遅延される

Page 24: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

24

終端操作?

● Stream#forEachやreduceやcollectなど

– Streamを返さないメソッド

● 遅延されていた中間操作を確定– 1回のループで済む

Page 25: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

25

StreamAPIで書くポイント

● ソース,中間操作,終端操作を意識する● forEachで副作用を与えて何でも書こうとしない

– forEachを使わずに実現する方法が存在する(はず)

– 出力などの副作用はOK

Page 26: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main4(再掲)

List<Integer>の

2で割り切れる

要素だけを一行ずつ出力

Page 27: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main4

List<Integer>の ソース2で割り切れる filter[中間操作]要素だけを一行ずつ出力 forEach[終端操作]

Page 28: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Collection<T>からStream<T>

● Collection<T>#stream()– parallelStream()で並列Streamも取得可

Page 29: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

29

filter[中間]

● filter(T -> boolean)– フィルタリング– 各要素を引数に適用しtrueを返したもののみ残す

– filterの引数には欲しい値の条件を書く

– s.filter(Objects::nonNull) // nullを除外

Stream<T>

IntStream

LongStream

DoubleStream

Page 30: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main5

値の変換を行う中間操作をしてみよう

Page 31: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main5

List<Person>から

Personの名前を

一行ずつ出力

Page 32: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main5

List<Person>から ソースPersonの名前を map[中間操作]一行ずつ出力 forEach[終端操作]

Page 33: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

33

map[中間操作]

● map(T -> R)– 写像・変換– 各要素を引数に適用した結果のStreamを作る

– personStream.map(p -> p.getName())

– seq.map(n -> n * 2)

Stream<T>

IntStream

LongStream

DoubleStream

Page 34: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

34

Streamを横断するmap

Stream<T> IntStream

LongStream DoubleStream

#mapToObj

#mapToInt

#mapToLong

#mapToDouble

#mapTo D

o uble

#ma pTo Lon g

#mapToInt

Page 35: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main6

複数の要素から一つの値へ

Page 36: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main6

ある数値の範囲の

総和を求める

乱数生成器Randomから

指定された個数の数値の

平均を求める

Page 37: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main6

ある数値の範囲の ソース総和を求める 終端操作乱数生成器Randomから ソース指定された個数の数値の 中間操作平均を求める 終端操作

Page 38: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

値の範囲からStream

● IntStream.range(start, end)– start〜end(end含まない)のIntStream

– end含める場合はrangeClosed

Page 39: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

39

reduce[終端]

● Optional<T> reduce((T, T) -> T)● T reduce(T, (T, T) -> T)● U reduce(U, (U, T) -> U, (U, U) -> U)

– 集約

– s.reduce((n, m) -> n < m ? n : m)● Optional

– 値が無いという状態を表すことができる

Stream<T>

IntStream

LongStream

DoubleStream

Page 40: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

40

reduce[終端]

Page 41: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

41

reduce[終端]

Page 42: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

42

reduce[終端]

Page 43: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

43

reduce[終端]

Page 44: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

44

reduce[終端]

Page 45: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

45

reduce[終端]

Page 46: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

46

並列reduce

Page 47: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

47

並列reduce

Page 48: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

48

並列reduce

Page 49: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

49

並列reduce

Page 50: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

50

並列reduce

Page 51: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

51

並列reduce6

Page 52: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

52

並列reduce6

Page 53: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

53

sum[終端]

● int sum()● long sum()● double sum()

– 総和を求める● そのものズバリ

– s.sum()

Stream<T>

IntStream

LongStream

DoubleStream

Page 54: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

54

summaryStatistics[終端]

● XxxSummaryStatistics

summaryStatistics()– 統計処理

● 合計,平均,最大,最小,個数

– stream.summaryStatistics().getSum()– 複数の統計的な値を求める場合

Stream<T>

IntStream

LongStream

DoubleStream

Page 55: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main6

ある数値の範囲の ソース

総和を求める 終端操作

乱数生成器Randomから ソース指定された個数の数値の 中間操作平均を求める 終端操作

Page 56: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

56

RandomからStream

● Random#doubles()– DoubleStream

– 無限のStream

– limitと併用

● Random#doubles(n)– n個の要素のDoubleStream

Page 57: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

57

Ave, max, min[終端]

● average(), max(), min()– Optional系を返す

– 平均,最大,最小値を求める

Stream<T>

IntStream

LongStream

DoubleStream

Page 58: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main6_1

Streamと並列処理

Page 59: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

59

Change the type of Stream

Sequential Stream

Parallel Streamparallel() sequential()

Page 60: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

60

Streamと並列処理

● 並列化したら早くなるのか– 一概には言えない・・・

● データ数依存● CPU依存● 操作依存

– 早くなるものもある– 並列化に際してはベンチマーク必須

Page 61: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Any question?

Page 62: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main7

ファイルからの読み出しとリストへの集約

Page 63: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main7

ファイルから

“{“か”}”で終わる行を

出力する

ファイルから

“{“か”}”で終わる行を

リストに入れる

Page 64: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main7

ファイルから

“{“か”}”で終わる行を

出力する

ファイルから

“{“か”}”で終わる行を

リストに入れる

Page 65: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main7

ファイルから ソース“{“か”}”で終わる行を 中間操作出力する 終端操作ファイルから ソース“{“か”}”で終わる行を 中間操作リストに入れる 終端操作

Page 66: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

ファイルからStream<String>

● BufferedReader#lines()● j.nio.file.Files#lines(Path)

– 一行ずつのStream– Closeしなければいけない

● twr利用可try (Stream<String> lines = Files.lines(path)) {

}

Page 67: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main7

ファイルから ソース

“{“か”}”で終わる行を 中間操作

出力する 終端操作

ファイルから ソース“{“か”}”で終わる行を 中間操作リストに入れる 終端操作

Page 68: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

68

collect

● R collect(() -> R, (R, T) -> R, (R, R) -> void)● R collect(Collector<T, ?, R>)

– 集約処理– R:集約先のオブジェクト

– () -> R:集約先のオブジェクトの生成

– (R, T) -> R:集約先のオブジェクトRに要素Tを集約

– (R, R) -> void:集約先のオブジェクトR同士のcombine● 並列処理用

Page 69: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

69

Collectors#toXxx

● toCollection()● toList()● toSet()

– リストなどへの集約をするCollectorの生成

Page 70: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main8

Streamを返す変換を平坦化する

Page 71: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main8

List<Campany>から

List<Car>を取得し

すべての車のうちの

軽自動車のみの

車名の

リストを作る

Page 72: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main8

List<Campany>から ソースList<Car>を取得し 中間操作すべての車のうちの

軽自動車のみの filter車名の mapリストを作る collect(toList())

Page 73: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Streamを返すmap

H e l l

H e l l o

H e l l o , W

H e l

Stream<Stream<R>>Stream<T>

Page 74: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

flatMap

H e l l

H e l l o

H e l l o , W

H e l

Stream<R>Stream<T>

Page 75: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

flatMap

● flatMap(T -> Stream<R>)– 写像・変換 + 平坦化

– 平坦化されたStream<R>

– companieStream.flatMap(c → c.getList().stream())

Page 76: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main9

文字列の連結

Page 77: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main9

List<Person>から

Personの名前を

,区切りで連結する

Page 78: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main9

List<Person>から ソースPersonの名前を 中間操作,区切りで連結する 終端操作

Page 79: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

79

Collectors#joining

● joining()● joining(delimiter)● joining(delimiter, prefix, suffix)

– 文字列の連結をするCollectorを返す

– 単なる連結だけでなく,デリミタなども指定可

Page 80: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main10

同じ値でグループ化

Page 81: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main10

List<People>から

同じ性別でPeopleをグループ化

List<People>から

同じ名字でPeopleの名前をグループ化

Page 82: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

82

Collectors#groupingBy

● groupingBy(T -> K)– Kの同じ値でグループ化

– Map<K, List<T>>にする

Page 83: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

83

Collectors#groupingBy

● groupingBy(T -> K, Collector)– 第二引数はMapの値へのCollector

– groupingBy(..., toSet()) : Map<K, Set<T>>

Page 84: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

84

Collectors#mapping

● mapping(T -> U, Collector)– 値の変換を行って集約するCollector

Page 85: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Main10_1

並列グループ化

Page 86: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

groupingBy vs. groupingByConcurrent

● groupingByでも並列化できる– 分割統治法– Map<K, List<V>>同士の連結が起きる

● 逐次よりも遅くなる可能性

● groupingByConcurrent– 一つのMap<K, List<V>>にマルチスレッドでアクセス

– 中間操作は並列● 中間操作があれば逐次よりも高速に

● どちらが良いかは,場合による

Page 87: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

87

ParallelgroupingBy

H e l l o , W o r l d !

Page 88: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

88

ParallelgroupingBy

Split!!

Page 89: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

89

ParallelgroupingBy

Map Map Map Map

Page 90: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

90

ParallelgroupingBy

Map Map Map MapMap Map

Page 91: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

91

ParallelgroupingBy

Map MapMap Map

Page 92: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

92

ParallelgroupingBy

Map Map

Page 93: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

93

H e l o , W o r l d !

Map

parallelgroupingByConcurrent

Page 94: きつねさんと学ぶ Lambda式&StreamAPIハンズオン[関ジャバ2015/7/11] #kanjava

Any question?