きつねさんと学ぶ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?