- 型パラメータ:
T
- リダクション操作の入力要素の型A
- リダクション操作の可変蓄積の型(通常は実装詳細として隠蔽される)R
- リダクション操作の結果の型
可変リダクション操作の例としては、Collection
への要素の蓄積、StringBuilder
を使用した文字列の連結、要素に関するサマリー情報(合計、最小、最大、平均など)の計算、ピボット・テーブル・サマリー(「販売業者別の最大金額取引」など)の計算、などが挙げられます。クラスCollectors
には、一般的な可変リダクションの実装が多数用意されています。
Collector
の仕様を決定する4つの関数は、互いに連携して動作することにより、可変結果コンテナにエントリを蓄積し、オプションでその結果に対して最終的な変換を実行します。 これらは次のとおりです。
- 新しい結果コンテナの作成(
supplier()
) - 結果コンテナへの新しいデータ要素の組み込み(
accumulator()
) - 2つの結果コンテナを1つに結合(
combiner()
) - (オプション)コンテナに対する最終的な変換の実行(
finisher()
)
また、コレクタにはCollector.Characteristics.CONCURRENT
などの一連の特性も含まれています。リダクション実装は、これらの特性によって提供されるヒントをパフォーマンス向上のために使用できます。
コレクタを使用したリダクションの順次実装は、サプライヤ関数を使って単一の結果コンテナを作成し、入力要素ごとに1回ずつアキュムレータ関数を呼び出します。 並列実装は、入力を分割し、パーティションごとに結果コンテナを作成し、各パーティションの内容をそのパーティション用のサブ結果内に蓄積した後、コンバイナ関数を使ってサブ結果をマージして1つの結果にまとめます。
順次実行と並列実行で同一の結果が得られるようにするには、コレクタの関数が同一性制約と結合性制約を満たす必要があります。
同一性制約とは、部分的に蓄積された任意の結果について、その結果を空の結果コンテナと結合したときに元と同じ結果が生成される必要がある、というものです。 つまり、一連のアキュムレータ呼出しやコンバイナ呼出しの結果として得られた、部分的に蓄積された結果a
について、a
はcombiner.apply(a, supplier.get())
と等しくなる必要があります。
結合性制約とは、計算を分割しても同一の結果が得られなければいけない、というものです。 つまり、任意の入力要素t1
とt2
について、以下の計算の結果r1
とr2
が等しくなる必要があります。
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つの結果a1
とa2
が等しくなるのは、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
- 関連項目:
-
ネストされたクラスのサマリー
-
メソッドのサマリー
修飾子と型メソッド説明BiConsumer<A,
T> 可変結果コンテナに値を折りたたむ関数。このCollectorの特性を示すCollector.Characteristics
のSet
を返します。combiner()
2つの部分的な結果を受け取ってマージする関数。finisher()
中間蓄積の型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) 指定されたsupplier
、accumulator
、combiner
、およびfinisher
関数で記述される新しいCollector
を返します。static <T,
R> Collector<T, R, R> of
(Supplier<R> supplier, BiConsumer<R, T> accumulator, BinaryOperator<R> combiner, Collector.Characteristics... characteristics) 指定されたsupplier
、accumulator
、およびcombiner
関数で記述される新しいCollector
を返します。supplier()
新しい可変結果コンテナを作成して返す関数。
-
メソッドの詳細
-
supplier
新しい可変結果コンテナを作成して返す関数。- 戻り値:
- 新しい可変結果コンテナを返す関数
-
accumulator
BiConsumer<A,T> accumulator()可変結果コンテナに値を折りたたむ関数。- 戻り値:
- 可変結果コンテナに値を折りたたむ関数
-
combiner
BinaryOperator<A> combiner()2つの部分的な結果を受け取ってマージする関数。 コンバイナ関数は、一方の引数の状態を他方の引数内に折りたたんでそれを返す可能性も、新しい結果コンテナを返す可能性もあります。- 戻り値:
- 2つの部分的な結果を結合して1つの合成結果を作成する関数
-
finisher
中間蓄積の型A
から最終結果の型R
への最終的な変換を実行します。特性
IDENTITY_FINISH
が設定されている場合、この関数は、A
からR
へのチェックされていないキャストを伴う恒等変換であると推定することができます。- 戻り値:
- 中間結果を最終結果に変換する関数
-
characteristics
Set<Collector.Characteristics> characteristics()このCollectorの特性を示すCollector.Characteristics
のSet
を返します。 このセットは不変であるべきです。- 戻り値:
- コレクタの特性の不変セット
-
of
static <T,R> Collector<T,R, ofR> (Supplier<R> supplier, BiConsumer<R, T> accumulator, BinaryOperator<R> combiner, Collector.Characteristics... characteristics) 指定されたsupplier
、accumulator
、およびcombiner
関数で記述される新しいCollector
を返します。 結果のCollector
は、Collector.Characteristics.IDENTITY_FINISH
特性を持ちます。- 型パラメータ:
T
- 新しいコレクタの入力要素の型R
- 新しいコレクタの中間蓄積結果と最終結果の型- パラメータ:
supplier
- 新しいコレクタのサプライヤ関数accumulator
- 新しいコレクタのアキュムレータ関数combiner
- 新しいコレクタのコンバイナ関数characteristics
- 新しいコレクタのコレクタ特性- 戻り値:
- 新しい
Collector
- 例外:
NullPointerException
- いずれかの引数がnullの場合
-
of
static <T,A, Collector<T,R> A, ofR> (Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Function<A, R> finisher, Collector.Characteristics... characteristics) 指定されたsupplier
、accumulator
、combiner
、およびfinisher
関数で記述される新しいCollector
を返します。- 型パラメータ:
T
- 新しいコレクタの入力要素の型A
- 新しいコレクタの中間蓄積の型R
- 新しいコレクタの最終結果の型- パラメータ:
supplier
- 新しいコレクタのサプライヤ関数accumulator
- 新しいコレクタのアキュムレータ関数combiner
- 新しいコレクタのコンバイナ関数finisher
- 新しいコレクタのフィニッシャ関数characteristics
- 新しいコレクタのコレクタ特性- 戻り値:
- 新しい
Collector
- 例外:
NullPointerException
- いずれかの引数がnullの場合
-