java8 lambda chapter5
DESCRIPTION
Java8 Lambdas勉強会 第5章TRANSCRIPT
![Page 1: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/1.jpg)
CHAPTER 5 Advanced Collections and Collectors
Kei Takinami
![Page 2: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/2.jpg)
Method References メソッド参照
Java8 から「 :: 」演算子を使ってメソッドを関数オブジェクトとして参照することができるようになった。
// インスタンスメソッド String#length()
Function<String, Integer> func1 = String::length;
assert func1.apply("Java 8") == 6;
インターフェイス: Function<T,R> 実装するメソッド: R apply(T t) 概要:実装するメソッドは、引数として T を受け取り、結果として R を返すものになる。
メモ
![Page 3: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/3.jpg)
Example1
例 ) アーティストの名前の一覧を取得したい場合。
artist -> artist.getName()
あくまでメソッド参照なので、最後の () はいらない。
メモ
メソッド参照の場合
ラムダ形式の場合
Artist::getName
![Page 4: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/4.jpg)
Example2
コンストラクタでも使える
(name, nationality) -> new Artist(name, nationality)
メソッド参照の場合
ラムダ形式の場合
Artist::new
![Page 5: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/5.jpg)
メソッド参照の形式
// static メソッドの場合public class Sample {
public static void main(String[] args) {
Function<String, String> func2 = Sample::add;
System.out.println(func2.apply(“test02")); // => [ ☆ test02 ☆]
}
public static String add(String value) {
return “[ " + value + " ]";☆ ☆ }
}
クラス名 :: メソッド名 (static メソッド )
インスタンス名 :: メソッド名 ( インスタンスメソッド )
![Page 6: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/6.jpg)
// インスタンスメソッドの場合public class Sample {
public static void main(String[] args) {
Sample sample = new Sample();
Function<String, String> func1 = sample::add;
System.out.println(func1.apply(“test01")); // => [ ☆ test01 ☆]
}
public static String add(String value) {
return “[ " + value + " ]";☆ ☆ }
}
![Page 7: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/7.jpg)
Element Ordering 要素の順番
順序が定義された Collection から Stream を生成するときにはその Stream も順序を持つ。
List<Integer> numbers = asList(1,2,3,4);
List<Integer> sameOrder = numbers.stream()
.collect(toList());
assertEquals(numbers, sameOrder); // => これは必ず OK
例 ) リストと Stream から生成された同じリストを比較
![Page 8: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/8.jpg)
逆に順序が定義されてない Collection( 例えば HashSet) から Stream を生成するときにはその Stream も順序を持たない。
Set<Integer> numbers = new HashSet<>(asList(4,3,2,1));
List<Integer> sameOrder = numbers.stream()
.collect(toList());
// NG になる場合があるassertEquals(asList(4,3,2,1) sameOrder);
例 ) Set と Stream から生成された同じリストを比較
![Page 9: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/9.jpg)
Stream の役割
Stream の役割はある Collection を他の Collection へ変換するだけじゃない。データに対していろんな処理ができるようにすることでもある。
Set<Integer> numbers = new HashSet<>(asList(4,3,2,1));
List<Integer> sameOrder = numbers.stream()
.sorted()
.collect(toList());// 前回と違って今回は必ず OK
assertEquals(asList(1,2,3,4), sameOrder);
例 ) 順番
![Page 10: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/10.jpg)
この順序は途中で処理を入れても維持される。例えば map 処理を途中に入れてみる。
List<Integer> numbers = asList(1,2,3,4);
List<Integer> stillOrdered = numbers.stream()
.map(x -> x + 1)
.collect(toList());
assertEquals(asList(2,3,4,5), stillOrdered); // => 必ず OK
例 ) map 処理で対象のリストをインクリメント
![Page 11: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/11.jpg)
ただ、順序があるから必ずいいというわけではない。順序があることによってコストがかかることもある。そういう場合は、 stream の unordered メソッドを使用
List<Integer> numbers = asList(1,2,3,4);
List<Integer> stillOrdered = numbers.stream()
. unordered () // 追加
.map(x -> x + 1)
.collect(toList());
assertEquals(asList(2,3,4,5), stillOrdered); // => 必ず OK
例 ) 前の例に unordered() を追加
でも実際、 filter,map,reduce は順序を維持した stream でもかなり高速に動作する。
メモ
![Page 12: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/12.jpg)
parallelstream のときだけは forEach は順番を保証しない。
List<String> list = Arrays.asList("list1", "list2", "list3", "list4", "list5");
// 順番通りに表示 => list1, list2, list3, list4, list5
list.stream().forEach(e -> System.out.println(e));
// ランダムに表示 => list3, list5, list4, list2, list1
list.parallelStream().forEach(e -> System.out.println(e));
並行処理の場合の順序
![Page 13: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/13.jpg)
その場合は forEachOrdered(Consumer<? super T> action)
List<String> list = Arrays.asList("list1", "list2", "list3", "list4", "list5");
// ランダムに表示 => list3, list5, list4, list2, list1
list.parallelStream().forEach(e -> System.out.println(e));
// ランダムに表示 => list3, list5, list4, list2, list1
list.parallelStream(). forEachOrdered(e -> System.out.println(e));
※ 注意順序は保証してくれるがパフォーマンスに影響があるのでparallelstream のときだけ使用するように。
![Page 14: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/14.jpg)
今まで collect(toList()) を使って List を作成しているけどmap も Set も使いたい。
Stream<String> s = Stream.of("a", "b", "c");
Set<String> set = s.collect(Collectors.toSet());
System.out.println(set);
Enter the Collector Collector について
toSet() も toMap() も toCollection もある
例 ) toSet()
![Page 15: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/15.jpg)
ただ、 toList のときも同様になんの List かわからない。
stream().collect(toCollection(TreeSet::new));
自動的に最適な実クラスを Stream ライブラリが設定していくれている。
例 ) TreeSet を指定
でも、ライブラリに任せないで自分でこのリストを使いたいときがある。
![Page 16: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/16.jpg)
collector を使って、ひとつの値を取得することもできる。
public static Optional<Artist> biggestGroup(Stream<Artist> artists) {
Function<Artist, Long> getCount = artist -> artist.getMembers()
.count();
// 一番メンバーの多いバンドを返却 return artists.collect(Collectors.maxBy(Comparator.comparing(getCount)));
}
To Values 値の変換
例 ) maxBy
・ maxBy
・ minBy
![Page 17: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/17.jpg)
もう少し簡単な例で maxBy
public static void sample01() {
Stream<Integer> stream = Stream.of(1,2,3,4,5);
Optional<Integer> maxNumber =
stream.collect(Collectors.maxBy(Comparator.naturalOrder()));
System.out.println(maxNumber.get());
}
結果: 5
public static void sample02() {
Stream<String> stream = Stream.of(“a”,“b”,“c”); // 文字列も OK
Optional<String> maxNumber =
stream .collect(Collectors.maxBy(Comparator.naturalOrder()));
System.out.println(maxNumber.get());
}
結果: c
![Page 18: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/18.jpg)
他にも合計とか平均も出せる便利なものもある【 1 】
// 合計を取得する場合 => summingInt
public static void sample01() {
Stream<Integer> stream = Stream.of(1,2,3,4,5);
ToIntFunction<Integer> func = x -> x;
// 合計を取得 int sum = stream.collect(Collectors.summingInt(func));
// 15 を出力 System.out.println(sum);
}
![Page 19: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/19.jpg)
他にも合計とか平均も出せる便利なものもある【 2 】
// オブジェクトを取得して、いろいろ計算public static void sample02() {
Stream<Integer> numberStream = Stream.of(1,2,3,4,5);
ToIntFunction<Integer> func = x -> x;
// 集計オブジェクトを取得 => summarizingInt で取得 IntSummaryStatistics sum = numberStream.collect(Collectors.summarizingInt(func));
// 合計を出力 (15)
System.out.println(sum.getSum());
// 要素数を出力 (5)
System.out.println(sum.getCount());
// 最大値を出力 (5)
System.out.println(sum.getMax());
// 最小値を出力 (1)
System.out.println(sum.getMin());
// 平均を出力 (3)
System.out.println(sum.getAverage());
}
![Page 20: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/20.jpg)
Stream を使って条件毎に2つのコレクションに変換したい場合がある。例)アーティストのリストをソロとバンドで分けたい場合
// partitioningBy メソッドを使って分割public Map<Boolean, List<Artist>> bandsAndSolo(Stream<Artist> artists) {
return artists.collect(Collectors.partitioningBy(artist -> artist.isSolo()));
}
結果: true=[SoloA,SoloB] false=[BandA]
// メソッド参照を使った場合public Map<Boolean, List<Artist>> bandsAndSolo(Stream<Artist> artists) {
return artists.collect(Collectors.partitioningBy(Artist::isSolo));
}
Partitoning the Data データの分割
partitioningBy を使えばおk。引数には Predicate を指定して、結果が true の場合と false の場合で場合分けしてくれる。
インターフェイス: Predicate<T>実装するメソッド: boolean test(T t) 概要:実装するメソッドは、引数として T を受け取り、 boolean 値を結果として返すものになる。
メモ
![Page 21: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/21.jpg)
true,false で分けるんじゃなくて、他にどんな値でもできる。例えば、アルバムをアーティスト毎に分類したい場合。
public Map<Artist, List<Album>> albumsByArtist(Stream<Album> albums) {
return albums.collect(Collectors.groupingBy(album -> album.getMainMusician()));
}
Grouping the Data データの分類
groupingBy を使えばおk。引数には Classifier を指定してあげる。
![Page 22: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/22.jpg)
Stream のデータを一般的に使う理由としては最終的に文字列を生成するため。 ( が多い )
Strings 文字列
![Page 23: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/23.jpg)
public static void main(String args[]) {
List<Artist> artists = new ArrayList<>();
artists.add(new Artist("George"));
artists.add(new Artist("Harrison"));
artists.add(new Artist("John"));
StringBuilder builder = new StringBuilder("[");
for (Artist artist : artists) {
if (builder.length() > 1) {
builder.append(",");
}
String name = artist.getName();
builder.append(name);
}
builder.append("]");
String result = builder.toString();
System.out.println(result); // => [George,Harrison,John]
}
例えばアーティスト名の一覧を出力したい場合
public static void main(String args[]) {
List<Artist> artists = new ArrayList<>();
artists.add(new Artist("George"));
artists.add(new Artist("Harrison"));
artists.add(new Artist("John"));
String result = artists.stream()
.map(Artist::getName)
.collect(Collectors.joining(“,”,“[”,“]”));
System.out.println(result); // => [George,Harrison,John]
}
Java8 以前の場合 Java8 の場合
Collectors.joining メソッドを使えばお k
Collectors.joining( 区切り文字 , プレフィックス , サフィックス ) といった感じ
![Page 24: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/24.jpg)
前回はアルバムをアーティスト毎に分類したが 今回は各アーティストのアルバムの数を集計したい。
Composing Collectors Collectors の合成
一番簡単な方法は一度グルーピングしてから、アルバム数をカウント。
![Page 25: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/25.jpg)
// アーティスト毎にグルーピング Map<Artist, List<Album>> albumsByArtist = albums.collect(groupingBy(album ->
album.getMainMusician()));
// グルーピングした Map から個数を取得 Map<Artist, Integer> numberOfAlbums = new HashMap<>();
for(Entry<Artist, List<Album>> entry : albumsByArtist.entrySet()) {
numberOfAlbums.put(entry.getKey(), entry.getValue().size()); // => [ アーティスト:枚数 ]
}
例 ) 一度グルーピングしてから、アルバム数をカウント。
// アーティスト毎にグルーピングpublic Map<Artist, Long> numberOfAlbums(Stream<Album> albums) {
return albums.collect(groupingBy(album ->
album.getMainMusician(), Collectors .counting()));
}
counting() を使って同じ処理を簡単に
![Page 26: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/26.jpg)
Stream<String> s = Stream.of("a", "bar", "c", "foo");
Map<Integer, String> m = s.collect(Collectors.groupingBy(t -> t.length()
, Collectors.joining()));
System.out.println(m); // => {1=ac, 3=barfoo}
他の例
こんな感じで groupingBy の第2引数の Collector を「 downstream collectors 」と呼ぶ
主に第一引数の結果になにかしらの処理を行いときに使う。averagingInt,summarizingLong, もそういった意味では「 downstream collectors 」の一種
![Page 27: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/27.jpg)
今度は個数じゃなく、アルバム名が欲しい場合
public Map<Artist, List<String>> nameOfAlbumsDumb(Stream<Album> albums) {
Map<Artist, List<Album>> albumsByArtist = albums.collect(
groupingBy(album ->album.getMainMusician()));
Map<Artist, List<String>> nameOfAlbums = new HashMap<>();
for(Entry<Artist, List<Album>> entry : albumsByArtist.entrySet()) {
nameOfAlbums.put(entry.getKey(), entry.getValue()
.stream()
.map(Album::getName)
.collect(toList()));
}
return nameOfAlbums;
}
前回同様グルーピングしてから、マッピング
![Page 28: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/28.jpg)
「 mapping 」を使って簡単に
public Map<Artist, List<String>> nameOfAlbums(Stream<Album> albums) {
return albums.collect(groupingBy(
Album::getMainMusician, mapping(Album::getName, toList())));
}
![Page 29: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/29.jpg)
java7 で書かれた以下のソースを独自の Stringjoiningcollector を使ってjava8 のソースへリファクタしていこう。※JDK 自体は今回作る collector を既に提供している。今回はあくまで、勉強のために。
StringBuilder builder = new StringBuilder("[");
for (Artist artist : artists) {
if (builder.length() > 1) {
builder.append(", ");
}
String name = artist.getName();
builder.append(name);
}
builder.append("]");
String result = builder.toString();
結果 = [aa,bb,cc] といった感じ
Refactoring and Custom Collectors リファクタリングと独自 Collectors
![Page 30: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/30.jpg)
第1ステップ
StringBuilder builder = new StringBuilder("[");
artists.stream()
.map(Artist::getName)
.forEach(name -> {
if (builder.length() > 1)
builder.append(", ");
builder.append(name);
});
builder.append("]");
String result = builder.toString();
stream と map を使った修正してみる
for 分の中が大きいし、まだまだ読みにくい。
![Page 31: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/31.jpg)
第 2 ステップ
StringBuilder reduced = artists.stream()
.map(Artist::getName)
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(name);
return builder;
}, (left, right) -> left.append(right));
reduced.insert(0, "[");
reduced.append("]");
String result = reduced.toString();
reduce を使ってみる
よけい読みにくく。。
![Page 32: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/32.jpg)
第 3 ステップ
StringCombiner combined = artists.stream()
.map(Artist::getName)
.reduce(new StringCombiner(", ", "[", "]"),
StringCombiner::add,
StringCombiner::merge);
String result = combined.toString();
独自の StringCombiner クラスを生成前回と違って、結合処理は全部このクラスに隠して実装
![Page 33: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/33.jpg)
第 3.1 ステップ
public StringCombiner add(String element) {
if (areAtStart()) {
builder.append(prefix);
} else {
builder.append(delim);
}
builder.append(element);
return this;
}
StringCombiner の add メソッドの中身
![Page 34: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/34.jpg)
第 3.2 ステップ
public StringCombiner merge(StringCombiner other) {
builder.append(other.builder);
return this;
}
StringCombiner の merge メソッドの中身
![Page 35: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/35.jpg)
第 3.3 ステップ
String combined = artists.stream()
.map(Artist::getName)
.reduce(new StringCombiner(", ", "[", "]"),
StringCombiner::add,
StringCombiner::merge)
.toString();
String result = combined;
最後のメソッドチェインになるように toStringを追加
![Page 36: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/36.jpg)
第 4 ステップ
String result = artists.stream()
.map(Artist::getName)
.collect(new StringCollector(", ", "[", "]"));
ただ、これを汎用的に使うのは難しい。なので汎用的に使えるように reduce 処理を Collector にしてしまう。今回それを StringCollector として新しく作成。
これで完全に独自 Collector を作ることができて、シンプルに。他の Collector とまったく同じ形式で使える。
![Page 37: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/37.jpg)
早速 StringCollector を実装してみる
![Page 38: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/38.jpg)
その前に。。。
![Page 39: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/39.jpg)
Collector インターフェイスを implement
独自 Collector の作り方
public interface Collector<T, A, R> {
~}
< 処理対象の型、実処理の型、返却値の型 >
例えば「 Collector<String, ?, Integer> 」だと、 Stream<String> から Integer を生成する。
![Page 40: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/40.jpg)
Collector インターフェイスに定義されている4 つのメソッドを実装してあげる。
メソッド名 戻り値の型 ラムダ式表現 内容supplier Supplier<A> () -> A → 前処理 要素の集積に使うオブジェクトを生成する
accumulator BiConsumer<A,T> (A, T) -> () → (A)集積 集積オブジェクト と、Stream 1の 要素を引数に、集積オブジェクトに要素を集積する
combiner BinaryOperator<A> (A,A) -> A → 2 1結合 並列処理の結果 つから つの結果にまとめる
finisher Function<A,R> A -> R → 後処理 集積オブジェクトを最後に変換する
![Page 41: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/41.jpg)
では、実際に StringCollector を実装
![Page 42: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/42.jpg)
1. supplier
public Supplier<StringCombiner> supplier() {
return () -> new StringCombiner(delim, prefix, suffix);
}
前に作った StringCombiner を使って
イメージ的には横の図パラレルで動作することもあるので Supplier は 2 つ。
supplier でコンテナオブジェクトを作成している。( ここでいうコンテナオブジェクトはStringCombiner)
![Page 43: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/43.jpg)
2.accumulator
public BiConsumer<StringCombiner, String> accumulator() {
return StringCombiner::add;
}
Supplier と同様に前に作った StringCombiner を使って
Stream の値をコンテナオブジェクトに追加している。
![Page 44: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/44.jpg)
3. combine
public BinaryOperator<StringCombiner> combiner() {
return StringCombiner::merge;
}
同様に前に作った StringCombiner を使って
combine で並行で処理していたものを統一。この際には新しい Container に登録している。
![Page 45: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/45.jpg)
4. finsher
public Function<StringCombiner, String> finisher() {
return StringCombiner::toString;
}
toString の値を最終的な戻り値として生成
finsher で最終的な成果物を生成
![Page 46: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/46.jpg)
完成
public class StringCollector implements Collector<String, StringCombiner, String> {
public Supplier<StringCombiner> supplier() {
return () -> new StringCombiner(delim, prefix, suffix);
}
public BiConsumer<StringCombiner, String> accumulator() {
return StringCombiner::add;
}
public BinaryOperator<StringCombiner> combiner() {
return StringCombiner::merge;
}
public Function<StringCombiner, String> finisher() {
return StringCombiner::toString;
}
}
![Page 47: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/47.jpg)
1.Map を使ってキャッシュしていた実装も ラムダが導入されたお陰でシンプルに。
Collection Niceties
public Artist getArtist (String name) {
Artist artist = artistCache.get(name);
if (artist == null) {
artist = readArtistFromDB(name);
artistCache.put(name, artist);
}
return artist;
}
Map に存在しない場合は DB に問い合わせるキャッシュの例
public Artist getArtist(String name) {
return artistCache.computeIfAbsent(name, this::readArtistFromDB);
}
computeIfAbsent メソッドで代替可能
![Page 48: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/48.jpg)
2.Map を使っての foreach 分もシンプルに。
Map<Artist, Integer> countOfAlbums = new HashMap<>();
for(Map.Entry<Artist, List<Album>> entry : albumsByArtist.entrySet()) {
Artist artist = entry.getKey();
List<Album> albums = entry.getValue();
countOfAlbums.put(artist, albums.size());
}
Map<Artist, Integer> countOfAlbums = new HashMap<>();
albumsByArtist.forEach((artist, albums) -> {
countOfAlbums.put(artist, albums.size());
});
![Page 49: Java8 Lambda chapter5](https://reader033.vdocuments.net/reader033/viewer/2022061118/5468f32faf7959a75e8b6f62/html5/thumbnails/49.jpg)
まとめ
・メソッド参照は簡単な記述でメソッドの参照ができる。
・ Collector は reduce メソッドの可変な analogue だし、 Stream の最終的な値を操作できる。
・ Java8 から様々な Collection を集計でき、独自 Collector も作成できるようになった。