arapon1201
@arapon1201

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

[1Z0-816]Java Gold 第4章 ストリームAPI 問22について

Q&A

Closed

解決したいこと

Java Gold 第4章 ストリームAPI 問22を理解したい。

mainメソッドの

list.stream().collect(new SampleCollector());

のcollectメソッドにSampleCollectorインスタンスを引数に与えた後の動きが理解できません。

SampleCollectorクラスで定義した
・supplier
・accumulator
・combiner
・finisher
・characteristics
の各メソッドはどのタイミングで呼び出されるのでしょうか?

SampleCollector

import java.util.EnumSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class SampleCollector
  implements Collector<String, StringBuilder, String>{
    @Override  //処理途中の値を保持するオブジェクトを生成するメソッド
    public Supplier<StringBuilder> supplier() {
      return StringBuilder::new;
    }
    @Override  //具体的に実行したい処理を記述したBiConsumer型のラムダ式を戻すメソッド
    public BiConsumer<StringBuilder, String> accumulator(){  //accumulatorメソッドを実装する無名クラス、戻り値はBiConsumer<StringBuilder, String>と定義
      return (builder, str) -> {
        if(builder.length() != 0){
          builder.append(",");
        }
        builder.append(str);
      };
    }
    @Override  //
    public BinaryOperator<StringBuilder> combiner(){
      return null;
    }
    @Override  //処理結果を戻すラムダ式を提供するメソッド
    public Function<StringBuilder, String> finisher(){
      return builder -> builder.toString();
    }
    @Override  //Collectorの特徴を表すEnumのセットを戻すメソッド
    public Set<Characteristics> characteristics(){
      return EnumSet.noneOf(Characteristics.class);
    }
}

CollectorSample

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class CollectorSample{
  public static void main(String[] args){
    List<String> list =
          Arrays.asList("A", "B", "C", "D", "E");

    String result =
      list.stream().collect(new SampleCollector());  //SampleCollectorインスタンスを引数に与える
    System.out.println(result);
  }
}

自分で試したこと

Java Gold解説確認
ドキュメントの確認
https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/stream/Stream.html#collect(java.util.stream.Collector)

0

3Answer

基本的な動きはお隣の collectメソッド にほぼ説明があります。

finisherやcharacteristicsなんかは Collectorインターフェースのドキュメント に詳しく記述があります。

そもそもの前提条件として一般的なコレクションのreduce(リダクション,還元)操作がピンとこないのであれば、そちらを先に理解した方が良いかもしれません。
※JavaのはちょっととっつきにくいかもしれないのでJavaScriptとか他言語で調べた方がよい可能性がなくもない

またcombinerとか一部のCharacteristicsで出てくる並列とか順序については、streamパッケージ全体 の解説を一読しておいた方がよいかもしれません。


自分で試した内容として「ドキュメントの確認」をされた、とのことですが
メソッドやクラスなど直接1件見てみるだけではなく、そこから関連するものを辿って調べないと分からない場合があります。
(今回のように複数引数を取るcollectの派生のような説明になっていたり、パッケージ全体としてstreamの挙動が前提になっている等)

0Like

Comments

  1. @arapon1201

    Questioner

    コメントありがとうございます。

    collect(Supplier, BiConsumer, BiConsumer)の引数として使用される関数をカプセル化したCollectorを使えば、コレクション方針の再利用やcollect操作の合成(マルチレベルのグループ化や分割など)が可能となります。
    →Collectorをカプセル化したSampleCollectorのこと

    インスタンス化したSampleCollectorをcollectメソッドに渡すと下記の処理が実行されるという認識であっておりますでしょうか?
    supplier - 新しい変更可能な結果コンテナを作成する関数。 並列実行の場合、この関数は複数回呼び出される可能性がありますが、そのたびに新しい値を返す必要があります。
    accumulator - associative、non-interfering、stateless関数は、要素を結果コンテナに折りたたむ必要があります。
    combiner - associative、non-interfering、stateless関数は、2つの部分結果コンテナを受け入れ、それらをマージします。これらのコンテナは、アキュムレータ関数と互換性がなければなりません。 コンバイナ関数は、第2の結果コンテナから第1の結果コンテナに要素を折りたたまなければなりません。

collect()メソッドはStream内の複数の要素を一つの要素にまとめる処理を行います。
CollectorSampleの例で言うと「"A", "B", "C", "D", "E"」の5つのStringを「"ABCDE"」の1つのStringにまとめる処理ですね。

処理の順番としては、まずsupplier()が呼ばれ、結果コンテナが作成されます。
次に、Stream内の各要素に対してaccumulator()が適用され、結果コンテナに各要素が格納されていきます。
最後にfinisher()が呼ばれ、結果コンテナからまとめた結果の要素を出力します。

combiner()は並列で処理している場合に使用します。
並列で処理している場合、各スレッド毎にsupplier()が呼ばれ、それぞれの結果コンテナを持ちます。
そのため、finisher()が呼ぶ前に、combiner()を使用して結果コンテナの1つにまとめる必要があります。

characteristics()についてはコメントの通り、「Collectorの特徴を表すEnumのセットを戻すメソッド」です。Collector.Characteristicsのドキュメントを参照してみてください。

Java Gold 第4章 ストリームAPI 問22がどのような問題だったか分からないため、この説明で十分かは分からないですがどうでしょうか。
また、私も浅い知識のため、間違えていたらすみません。
collect()メソッドの実装を実際に追ってみるのも手かと思います。

0Like

コメントしていただいた方の説明と再度ドキュメント読み直したら理解することができました!
ありがとうございます!
こちらはクローズさせていただきます。

0Like

Your answer might help someone💌