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

Post on 16-Aug-2015

243 views 3 download

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

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

2015/7/11 @ 関ジャバ

2

Who are you?

吉田真也(@bitter_fox, @shinyafox)

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

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

● OpenJDKコミッタ– Project Kulla

3

Project Kulla

ハンズオンの諸注意

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

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

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

Main1

ラムダ式導入

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

ラムダ式

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

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

Main2

関数型インターフェース

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

関数型インターフェース

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

interface F {

void exec0();

}

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

Any question?

Main3

内部イテレーションへ

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

Main3

List<Integer>の要素を

一行ずつ出力

for (Integer n : list) {

System.out.println(n);

}

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

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

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

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

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

Iterable#forEach

list.forEach(n ->

System.out.println(n));

Main4

StreamAPI導入

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

Main4

List<Integer>の

2で割り切れる

要素だけを一行ずつ出力

StreamAPI

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

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

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

21

StreamAPI

java.util.stream.

Stream<T>

IntStream

LongStream

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

22

Collection

配列

BufferReader

etc...

Stream

IntStream

LongStream

DoubleStream

中間操作

終端操作

j.u.stream.*Source

23

中間操作?

● Stream#filterやmapなど

– Streamを返すメソッド

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

24

終端操作?

● Stream#forEachやreduceやcollectなど

– Streamを返さないメソッド

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

25

StreamAPIで書くポイント

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

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

– 出力などの副作用はOK

Main4(再掲)

List<Integer>の

2で割り切れる

要素だけを一行ずつ出力

Main4

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

Collection<T>からStream<T>

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

29

filter[中間]

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

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

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

Stream<T>

IntStream

LongStream

DoubleStream

Main5

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

Main5

List<Person>から

Personの名前を

一行ずつ出力

Main5

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

33

map[中間操作]

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

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

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

Stream<T>

IntStream

LongStream

DoubleStream

34

Streamを横断するmap

Stream<T> IntStream

LongStream DoubleStream

#mapToObj

#mapToInt

#mapToLong

#mapToDouble

#mapTo D

o uble

#ma pTo Lon g

#mapToInt

Main6

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

Main6

ある数値の範囲の

総和を求める

乱数生成器Randomから

指定された個数の数値の

平均を求める

Main6

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

値の範囲からStream

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

– end含める場合はrangeClosed

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

40

reduce[終端]

41

reduce[終端]

42

reduce[終端]

43

reduce[終端]

44

reduce[終端]

45

reduce[終端]

46

並列reduce

47

並列reduce

48

並列reduce

49

並列reduce

50

並列reduce

51

並列reduce6

52

並列reduce6

53

sum[終端]

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

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

– s.sum()

Stream<T>

IntStream

LongStream

DoubleStream

54

summaryStatistics[終端]

● XxxSummaryStatistics

summaryStatistics()– 統計処理

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

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

Stream<T>

IntStream

LongStream

DoubleStream

Main6

ある数値の範囲の ソース

総和を求める 終端操作

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

56

RandomからStream

● Random#doubles()– DoubleStream

– 無限のStream

– limitと併用

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

57

Ave, max, min[終端]

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

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

Stream<T>

IntStream

LongStream

DoubleStream

Main6_1

Streamと並列処理

59

Change the type of Stream

Sequential Stream

Parallel Streamparallel() sequential()

60

Streamと並列処理

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

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

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

Any question?

Main7

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

Main7

ファイルから

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

出力する

ファイルから

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

リストに入れる

Main7

ファイルから

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

出力する

ファイルから

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

リストに入れる

Main7

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

ファイルからStream<String>

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

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

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

}

Main7

ファイルから ソース

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

出力する 終端操作

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

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● 並列処理用

69

Collectors#toXxx

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

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

Main8

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

Main8

List<Campany>から

List<Car>を取得し

すべての車のうちの

軽自動車のみの

車名の

リストを作る

Main8

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

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

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>

flatMap

H e l l

H e l l o

H e l l o , W

H e l

Stream<R>Stream<T>

flatMap

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

– 平坦化されたStream<R>

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

Main9

文字列の連結

Main9

List<Person>から

Personの名前を

,区切りで連結する

Main9

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

79

Collectors#joining

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

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

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

Main10

同じ値でグループ化

Main10

List<People>から

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

List<People>から

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

82

Collectors#groupingBy

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

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

83

Collectors#groupingBy

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

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

84

Collectors#mapping

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

Main10_1

並列グループ化

groupingBy vs. groupingByConcurrent

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

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

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

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

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

87

ParallelgroupingBy

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

88

ParallelgroupingBy

Split!!

89

ParallelgroupingBy

Map Map Map Map

90

ParallelgroupingBy

Map Map Map MapMap Map

91

ParallelgroupingBy

Map MapMap Map

92

ParallelgroupingBy

Map Map

93

H e l o , W o r l d !

Map

parallelgroupingByConcurrent

Any question?