8. What’s Actually Going On (2)
• Example 3-4 Just the filter, no collect step
• Example 3-5 Just the filter, no collect step
• Example 3-6 Printing out artist names
allArtists.stream()
.filter( artist -> artist.isFrom("London") );
allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London");
});
long count = allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London");
})
.count();
printlnが実行されない
printlnが実行される
8
9. Common Stream Operations
• Stream のメソッドの使用例:
– collect
– map
– filter
– flatMap
– min(または max)
– reduce
• A Common Pattern Appears
• Putting Operations Together
9
13. filter
• Example 3-10 数字から始まる文字を検索: forループ と if文 使用
• Example 3-11 数字から始まる文字を検索: 「Functional style」 filter使用
– if文と同じ働きをする1個のfunction を lamda式でfilterの引数に指定
– 戻り値: true OR false
– Figure 3-6. The Predicate interface
List<String> beginningWithNumbers = new ArrayList<>();
for(String value : Arrays.asList("a", "1abc", "abc1")) {
if ( Character.isDigit(value.charAt(0)) ) {
beginningWithNumbers.add(value);
}
}
assertEquals(Arrays.asList("1abc"), beginningWithNumbers);
List<String> beginningWithNumbers
= Stream.of( "a", "1abc", "abc1" )
.filter( value -> Character.isDigit(value.charAt(0)) )
.collect( Collectors.toList() );
assertEquals(Arrays.asList("1abc"), beginningWithNumbers);
13
14. flatMap
• 値 を Stream に変換
• 変換後の全てのStreamを連結
• Example 3-12. Stream list
• mapとの共通点:
– Function を引数にとる
• mapとの違い:
– Function の戻り値(R) は Stream に制限される
List<Integer> together = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4) )
.flatMap(numbers -> numbers.stream())
.collect(Collectors.toList());
assertEquals(Arrays.asList(1, 2, 3, 4), together);
(Map) a value -> a new value
(flatMap) a value -> a new Stream
List<Integer>
-> Stream<Integer>
に変換
(Collection#stream メ
ソッドを使って )
14
15. max and min
• Example 3-13. 長さが一番短い曲を検索: min 使用
• min や max を考えるのに、 「順序」 を考える必要がある
– 「順序」 をStreamに伝える → Comparator を使用
• この例では、順序に track.getLength を使用
• java8 で Comparator クラスに comparing という static メソッドが追加された
– 引数も戻り値もfunction
• 引数: Function
• 戻り値: Comparator
• min, max の戻り値: Optional (-> 第4章参照)
– get メソッドで値を取得
List<Track> tracks = Arrays.asList(new Track("Bakai", 524),
new Track("Violets for Your Furs", 378),
new Track("Time Was", 451));
Track shortestTrack = tracks.stream()
.min( Comparator.comparing( track -> track.getLength() ) )
.get();
assertEquals(tracks.get(1), shortestTrack);
15
16. A Common Pattern Appears
• Example 3-14. 長さが一番短い曲を検索: for ループ使用 (Example 3-13を書き換え)
– 変数(shortestTrack )を初期化
– for ループで if文
• If( 変数 と currnetの要素を比較。currentの要素の方が短ければ ) { 変数を書き換え }
– 最終的に 変数(shortestTrack )に一番短い曲が格納されている
• Example 3-15. The reduce pattern (擬似コード)
– If文の代わりに、combine 関数を呼び出し
• 一番短い曲を求める場合は combine は accumulator とcurrnetの要素を比較 して、短い方を返す
• この一般的なパターンが Streams API の operation で体系化されている
Track shortestTrack = tracks.get(0);
for (Track track : tracks) {
if (track.getLength() < shortestTrack.getLength()) {
shortestTrack = track;
}
}
Object accumulator = initialValue;
for(Object element : collection) {
accumulator = combine(accumulator, element);
}
16
18. reduce (2)
• Example 3-18. 要素の足し算. Imperative implementation
– 変数の全ての更新を手動で管理
int acc = 0;
for (Integer element : Arrays.asList(1, 2, 3)) {
acc = acc + element;
}
assertEquals(6, acc);
18
19. Putting Operations Together
• 課題を、シンプルなStreamのoperationに分解
– (例)あるアルバムの中の band の国籍一覧を求める
• アルバムの中のミュージシャン一覧から
• 名前が "The" から始まるミュージシャンを band として扱い
• その band の国籍を求め、
• 国籍一覧を返す
• この domain クラス(Album) には 戻り値がStream のメソッドがあるが、それが無いクラスの場合
– List or Set でOK。 List or Set (Collection) に stream メソッドがあるため。
• domain クラスをカプセル化するのに…
– 戻り値がStream のメソッドを公開するほうが better
• ↑ List or Setが戻り値のメソッドを公開するよりも
• 内部の List or Set に影響を与えないため
Set<String> origins = album.getMusicians()
.filter( artist -> artist.getName().startsWith("The") )
.map( artist -> artist.getNationality() )
.collect( Collectors.toSet() );
戻り値: Stream (lazy)
Artist -> String に変換して
新しい Stream を返す
19