モジュール java.base
パッケージ java.util.stream

インタフェースCollector<T,A,R>

型パラメータ:
T - リダクション操作の入力要素の型
A - リダクション操作の可変蓄積の型(通常は実装詳細として隠蔽される)
R - リダクション操作の結果の型

public interface Collector<T,A,R>
可変結果コンテナに入力要素を蓄積し、オプションですべての入力要素が処理された後で蓄積された結果を最終的な表現に変換する可変リダクション操作 リダクション操作は、順次に実行することも並列に実行することもできます。

可変リダクション操作の例としては、Collectionへの要素の蓄積、StringBuilderを使用した文字列の連結、要素に関するサマリー情報(合計、最小、最大、平均など)の計算、ピボット・テーブル・サマリー(「販売業者別の最大金額取引」など)の計算、などが挙げられます。クラスCollectorsには、一般的な可変リダクションの実装が多数用意されています。

Collectorの仕様を決定する4つの関数は、互いに連携して動作することにより、可変結果コンテナにエントリを蓄積し、オプションでその結果に対して最終的な変換を実行します。 これらは次のとおりです。

  • 新しい結果コンテナの作成(supplier())
  • 結果コンテナへの新しいデータ要素の組み込み(accumulator())
  • 2つの結果コンテナを1つに結合(combiner())
  • (オプション)コンテナに対する最終的な変換の実行(finisher())

また、コレクタにはCollector.Characteristics.CONCURRENTなどの一連の特性も含まれています。リダクション実装は、これらの特性によって提供されるヒントをパフォーマンス向上のために使用できます。

コレクタを使用したリダクションの順次実装は、サプライヤ関数を使って単一の結果コンテナを作成し、入力要素ごとに1回ずつアキュムレータ関数を呼び出します。 並列実装は、入力を分割し、パーティションごとに結果コンテナを作成し、各パーティションの内容をそのパーティション用のサブ結果内に蓄積した後、コンバイナ関数を使ってサブ結果をマージして1つの結果にまとめます。

順次実行と並列実行で同一の結果が得られるようにするには、コレクタの関数が同一性制約と結合性制約を満たす必要があります。

同一性制約とは、部分的に蓄積された任意の結果について、その結果を空の結果コンテナと結合したときに元と同じ結果が生成される必要がある、というものです。 つまり、一連のアキュムレータ呼出しやコンバイナ呼出しの結果として得られた、部分的に蓄積された結果aについて、acombiner.apply(a, supplier.get())と等しくなる必要があります。

結合性制約とは、計算を分割しても同一の結果が得られなければいけない、というものです。 つまり、任意の入力要素t1t2について、以下の計算の結果r1r2が等しくなる必要があります。


     A a1 = supplier.get();
     accumulator.accept(a1, t1);
     accumulator.accept(a1, t2);
     R r1 = finisher.apply(a1);  // result without splitting

     A a2 = supplier.get();
     accumulator.accept(a2, t1);
     A a3 = supplier.get();
     accumulator.accept(a3, t2);
     R r2 = finisher.apply(combiner.apply(a2, a3));  // result with splitting
  

UNORDERED特性を持たないコレクタでは、蓄積された2つの結果a1a2が等しくなるのは、finisher.apply(a1).equals(finisher.apply(a2))の場合になります。 順序付けされていないコレクタでは、等価性の条件が緩和され、順序の違いに関する非等価が許容されるようになります。 (たとえば、要素をListに蓄積した順序付けされていないコレクタは、2つのリストが同じ要素を含んでいれば両者を等しいとみなし、順序は無視します。)

Stream.collect(Collector)のような、Collectorに基づいてリダクションを実装しているライブラリは、次の制約に従う必要があります。

  • アキュムレータ関数に渡される第1引数、コンバイナ関数に渡される両方の引数、およびフィニッシャ関数に渡される引数は、結果サプライヤ、アキュムレータ、またはコンバイナ関数を事前に呼び出した際の結果でなければいけない。
  • 実装は、結果サプライヤ、アキュムレータ、またはコンバイナ関数のいずれの結果についても、やはりアキュムレータ、コンバイナ、またはフィニッシャ関数に渡すかリダクション操作の呼出し元に返すだけにとどめ、それ以外は何も行わないようにすべきである。
  • ある結果がコンバイナ関数またはフィニッシャ関数に渡され、その関数から同じオブジェクトが返されない場合、それは二度と使用されない。
  • いったんコンバイナ関数またはフィニッシャ関数に渡された結果は、二度とアキュムレータ関数に渡されることはない。
  • 非並行コレクタの場合、結果サプライヤ、アキュムレータ、またはコンバイナ関数から返された結果はすべて、スレッド内にシリアルに閉じこめる必要がある。 そうすれば、Collectorに追加の同期を実装しなくても収集を並列に行える。 リダクション実装は、入力が正しく分割され、各パーティションが隔離状態で処理され、蓄積完了後にのみ結合が実行されるように管理する必要がある。
  • 並行コレクタの場合、実装は自由に並行リダクションを実施できる(ただし必須ではない)。 並行リダクションとは、蓄積中に結果が隔離されることなく、同時に変更可能な同じ結果コンテナを使って複数のスレッドからアキュムレータ関数が同時に呼び出されるようなリダクションである。 並行リダクションを適用するのは、コレクタがCollector.Characteristics.UNORDERED特性を持つ場合、または元となるデータが順序付けされていない場合だけにすべきである。

Collectorsの定義済の実装のほかに、staticファクトリメソッドof(Supplier, BiConsumer, BinaryOperator, Characteristics...)を使っても、コレクタを構築できます。 たとえば、ウィジェットをTreeSet内に蓄積するコレクタは、次のようにして作成できます。


     Collector<Widget, ?, TreeSet<Widget>> intoSet =
         Collector.of(TreeSet::new, TreeSet::add,
                      (left, right) -> { left.addAll(right); return left; });
 
(この動作は、定義済のコレクタCollectors.toCollection(Supplier)でも実装されています)。

APIのノート:
Collectorによるリダクション操作の実行時には、次と同等の結果が生成されるべきです。

     A container = collector.supplier().get();
     for (T t : data)
         collector.accumulator().accept(container, t);
     return collector.finisher().apply(container);
 

ただしライブラリは、入力を自由に分割し、パーティションごとにリダクションを実行した後、コンバイナ関数を使って部分的な結果を結合することにより、並列リダクションを実現することができます。 (そのパフォーマンスの善し悪しは、実際のリダクション操作ごとに異なる可能性があります。具体的には、アキュムレータ関数やコンバイナ関数の相対的なコストによって決まります。)

コレクタは合成可能なように設計されています。Collectorsのメソッドの多くは、あるコレクタを受け取って新しいコレクタを生成する関数になっています。 たとえば、従業員ストリームの給料の合計を計算する次のようなコレクタがあるとします。


     Collector<Employee, ?, Integer> summingSalaries
         = Collectors.summingInt(Employee::getSalary))
 
給料の部門別合計の表を作成するコレクタを作成する場合、Collectors.groupingBy(Function, Collector)を使えば「給料の合計」のロジックを再利用できます。

     Collector<Employee, ?, Map<Department, Integer>> summingSalariesByDept
         = Collectors.groupingBy(Employee::getDepartment, summingSalaries);
 

導入されたバージョン:
1.8
関連項目:
  • ネストされたクラスのサマリー

    ネストされたクラス
    修飾子と型
    インタフェース
    説明
    static enum 
    リダクション実装の最適化に使用可能な、Collectorのプロパティーを示す特性。
  • メソッドのサマリー

    修飾子と型
    メソッド
    説明
    可変結果コンテナに値を折りたたむ関数。
    このCollectorの特性を示すCollector.CharacteristicsSetを返します。
    2つの部分的な結果を受け取ってマージする関数。
    中間蓄積の型Aから最終結果の型Rへの最終的な変換を実行します。
    static <T, A, R> Collector<T,A,R>
    of(Supplier<A> supplier, BiConsumer<A,T> accumulator, BinaryOperator<A> combiner, Function<A,R> finisher, Collector.Characteristics... characteristics)
    指定されたsupplieraccumulatorcombiner、およびfinisher関数で記述される新しいCollectorを返します。
    static <T, R> Collector<T,R,R>
    of(Supplier<R> supplier, BiConsumer<R,T> accumulator, BinaryOperator<R> combiner, Collector.Characteristics... characteristics)
    指定されたsupplieraccumulator、およびcombiner関数で記述される新しいCollectorを返します。
    新しい可変結果コンテナを作成して返す関数。
  • メソッドの詳細

    • supplier

      Supplier<A> supplier()
      新しい可変結果コンテナを作成して返す関数。
      戻り値:
      新しい可変結果コンテナを返す関数
    • accumulator

      BiConsumer<A,T> accumulator()
      可変結果コンテナに値を折りたたむ関数。
      戻り値:
      可変結果コンテナに値を折りたたむ関数
    • combiner

      BinaryOperator<A> combiner()
      2つの部分的な結果を受け取ってマージする関数。 コンバイナ関数は、一方の引数の状態を他方の引数内に折りたたんでそれを返す可能性も、新しい結果コンテナを返す可能性もあります。
      戻り値:
      2つの部分的な結果を結合して1つの合成結果を作成する関数
    • finisher

      Function<A,R> finisher()
      中間蓄積の型Aから最終結果の型Rへの最終的な変換を実行します。

      特性IDENTITY_FINISHが設定されている場合、この関数は、AからRへのチェックされていないキャストを伴う恒等変換であると推定することができます。

      戻り値:
      中間結果を最終結果に変換する関数
    • characteristics

      Set<Collector.Characteristics> characteristics()
      このCollectorの特性を示すCollector.CharacteristicsSetを返します。 このセットは不変であるべきです。
      戻り値:
      コレクタの特性の不変セット
    • of

      static <T, R> Collector<T,R,R> of(Supplier<R> supplier, BiConsumer<R,T> accumulator, BinaryOperator<R> combiner, Collector.Characteristics... characteristics)
      指定されたsupplieraccumulator、およびcombiner関数で記述される新しいCollectorを返します。 結果のCollectorは、Collector.Characteristics.IDENTITY_FINISH特性を持ちます。
      型パラメータ:
      T - 新しいコレクタの入力要素の型
      R - 新しいコレクタの中間蓄積結果と最終結果の型
      パラメータ:
      supplier - 新しいコレクタのサプライヤ関数
      accumulator - 新しいコレクタのアキュムレータ関数
      combiner - 新しいコレクタのコンバイナ関数
      characteristics - 新しいコレクタのコレクタ特性
      戻り値:
      新しいCollector
      例外:
      NullPointerException - いずれかの引数がnullの場合
    • of

      static <T, A, R> Collector<T,A,R> of(Supplier<A> supplier, BiConsumer<A,T> accumulator, BinaryOperator<A> combiner, Function<A,R> finisher, Collector.Characteristics... characteristics)
      指定されたsupplieraccumulatorcombiner、およびfinisher関数で記述される新しいCollectorを返します。
      型パラメータ:
      T - 新しいコレクタの入力要素の型
      A - 新しいコレクタの中間蓄積の型
      R - 新しいコレクタの最終結果の型
      パラメータ:
      supplier - 新しいコレクタのサプライヤ関数
      accumulator - 新しいコレクタのアキュムレータ関数
      combiner - 新しいコレクタのコンバイナ関数
      finisher - 新しいコレクタのフィニッシャ関数
      characteristics - 新しいコレクタのコレクタ特性
      戻り値:
      新しいCollector
      例外:
      NullPointerException - いずれかの引数がnullの場合