1概要
このチュートリアルでは、
Stream
を処理する最後のステップで使用されるJava 8のCollectorsを調べます。
Stream
API自体についてもっと知りたい場合は、リンク:/java-8-streams[この記事]をチェックしてください。
2
Stream.collect()
メソッド
Stream.collect()
は、Java 8の
Stream API
のターミナルメソッドの1つです。それは、
Stream
インスタンスに保持されているデータ要素に対して可変フォールド操作(いくつかのデータ構造への要素の再パッケージ化、いくつかの追加ロジックの適用、それらの連結など)を実行することを可能にする。
この操作の戦略は、
Collector
インターフェース実装を通じて提供されます。
3
コレクター
定義済みの実装はすべて
Collectors
クラスにあります。
読みやすさを向上させるために、次の静的インポートを使用するのが一般的です。
import static java.util.stream.Collectors.** ;
またはあなたが選んだ単一の輸入コレクター:
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
次の例では、次のリストを再利用します。
List<String> givenList = Arrays.asList("a", "bb", "ccc", "dd");
3.1.
Collectors.toList()
ToList
コレクターは、すべての
Stream
エレメントを
List
インスタンスに収集するために使用できます。覚えておくべき重要なことは、このメソッドでは特定の
List
実装を想定できないということです。もっと細かく制御したい場合は、代わりに
toCollection
を使用してください。
一連の要素を表す
Stream
インスタンスを作成し、それらを
List
インスタンスにまとめます。
List<String> result = givenList.stream()
.collect(toList());
3.2.
Collectors.toSet()
ToSet
コレクターは、すべての
Stream
要素を
Set
インスタンスに収集するために使用できます。覚えておくべき重要なことは、このメソッドでは特定の
Set
実装を想定できないということです。もっと細かく制御したい場合は、代わりに
toCollection
を使用してください。
一連の要素を表すStreamインスタンスを作成し、それらを
Set
インスタンスにまとめます。
Set<String> result = givenList.stream()
.collect(toSet());
3.3.
Collectors.toCollection()
すでにお気づきかもしれませんが、
toSetおよびtoList
コレクターを使用するときは、それらの実装をまったく想定できません。カスタム実装を使用したい場合は、提供されているコレクションの中から
toCollection
コレクターを使用する必要があります。
一連の要素を表す
Stream
インスタンスを作成し、それらを
LinkedList
インスタンスにまとめます。
List<String> result = givenList.stream()
.collect(toCollection(LinkedList::new))
これが不変コレクションでは動作しないことに注意してください。そのような場合は、カスタムの
Collector
実装を作成するか、
collectingAndThen
を使用する必要があります。
3.4.
Collectors
.t
oMap()
ToMap
コレクターは、
Stream
エレメントを収集して
Map
インスタンスにするために使用できます。これを行うには、2つの機能を提供する必要があります。
-
keyMapper
-
valueMapper
keyMapper
は、
Stream
要素から
Map
キーを抽出するために使用され、
valueMapper
は、特定のキーに関連付けられている値を抽出するために使用されます。
文字列をキーとして、長さを値として格納するMapにこれらの要素を集めましょう。
Map<String, Integer> result = givenList.stream()
.collect(toMap(Function.identity(), String::length))
Function.identity()
は、同じ値を受け入れて返す関数を定義するための単なるショートカットです。
時々、あなたはあなたがキー衝突に終わるかもしれない状況に遭遇するかもしれません。そのような場合は、
toMap
を別の署名と共に使用する必要があります。
Map<String, Integer> result = givenList.stream()
.collect(toMap(Function.identity(), String::length, (i1, i2) -> i1));
ここでの3番目の引数は
BinaryOperator
です。ここでは、衝突の処理方法を指定できます。この場合、同じ文字列も常に同じ長さを持つことがわかっているため、これら2つの衝突する値のいずれかを選択します。
3.5.
Collectors
.c
ollectingAndThen()
CollectingAndThen
は、収集終了後に結果に対して別のアクションを実行できるようにする特別なコレクターです。
Stream
要素を集めて
List
インスタンスにしてから、結果を
ImmutableList
インスタンスに変換しましょう。
List<String> result = givenList.stream()
.collect(collectingAndThen(toList(), ImmutableList::copyOf))
3.6.
Collectors
.j
oining()
Joining
コレクターは、
Stream <String>
要素を結合するために使用できます。
次のようにして、一緒に参加できます
String result = givenList.stream()
.collect(joining());
これは次のようになります。
"abbcccdd"
カスタムの区切り文字、プレフィックス、ポストフィックスも指定できます。
String result = givenList.stream()
.collect(joining(" "));
これは次のようになります。
"a bb ccc dd"
またはあなたが書くことができます:
String result = givenList.stream()
.collect(joining(" ", "PRE-", "-POST"));
これは次のようになります。
"PRE-a bb ccc dd-POST"
3.7.
Collectors
.c
ounting()
Counting
は、すべての
Stream
要素を単純にカウントできる単純なコレクターです。
今書くことができます:
Long result = givenList.stream()
.collect(counting());
3.8.
Collectors
.s
ummarizingDouble/Long/Int()
SummarizingDouble/Long/Int
は、抽出された要素の
Stream
内の数値データに関する統計情報を含む特別なクラスを返すコレクターです。
文字列の長さに関する情報は、次のようにして取得できます。
DoubleSummaryStatistics result = givenList.stream()
.collect(summarizingDouble(String::length));
この場合、以下が当てはまります。
assertThat(result.getAverage()).isEqualTo(2);
assertThat(result.getCount()).isEqualTo(4);
assertThat(result.getMax()).isEqualTo(3);
assertThat(result.getMin()).isEqualTo(1);
assertThat(result.getSum()).isEqualTo(8);
3.9.
Collectors.averagingDouble/Long/Int()
AveragingDouble/Long/Int
は、抽出された要素の平均を単に返すコレクタです。
次のようにして、平均文字列長を取得できます。
Double result = givenList.stream()
.collect(averagingDouble(String::length));
** 3.10.
Collectors
.s__ummingDouble/Long/Int()
__ **
SummingDouble/Long/Int
は、抽出された要素の合計を単純に返すコレクタです。
次のようにして、すべての文字列長の合計を求めることができます。
Double result = givenList.stream()
.collect(summingDouble(String::length));
3.11.
Collectors.maxBy()/minBy()
MaxBy
/
MinBy
コレクターは、提供された
Comparator
インスタンスに従って、
Stream
の最大/最小要素を返します。
次のようにして最大の要素を選ぶことができます。
Optional<String> result = givenList.stream()
.collect(maxBy(Comparator.naturalOrder()));
戻り値が
Optional
インスタンスにラップされていることに注意してください。これにより、ユーザーは空のコレクションコーナーケースを再考する必要があります。
3.12.
Collectors
.
groupingBy()
GroupingBy
コレクターは、オブジェクトをいくつかのプロパティでグループ化し、結果を
Map
インスタンスに格納するために使用されます。
文字列の長さでそれらをグループ化し、グループ化結果を
Set
インスタンスに格納できます。
Map<Integer, Set<String>> result = givenList.stream()
.collect(groupingBy(String::length, toSet()));
これにより、次のことが成り立ちます。
assertThat(result)
.containsEntry(1, newHashSet("a"))
.containsEntry(2, newHashSet("bb", "dd"))
.containsEntry(3, newHashSet("ccc"));
groupingBy
メソッドの2番目の引数は
Collector
であり、任意の
Collector
を自由に使用できます。
3.13.
Collectors.partitioningBy()
PartitioningBy
は
groupingBy
の特殊なケースで、
Predicate
インスタンスを受け入れ、
Stream
要素を
Boolean
値をキーとして、およびコレクションを値として格納する
Map
インスタンスに収集します。
“ true”キーの下には、指定された
Predicate
に一致する要素のコレクションがあり、また“ false”キーの下には、指定された
Predicate
に一致しない要素のコレクションがあります。
あなたは書ける:
Map<Boolean, List<String>> result = givenList.stream()
.collect(partitioningBy(s -> s.length() > 2))
次のものを含むMapになります。
{false=["a", "bb", "dd"], true=["ccc"]}
4カスタムコレクター
Collectorの実装を書きたい場合は、Collectorインターフェースを実装し、その3つの汎用パラメーターを指定する必要があります。
public interface Collector<T, A, R> {...}
-
T
– 収集に利用できるオブジェクトの種類 -
A
– 可変アキュムレータオブジェクトの型 -
R
– 最終結果のタイプ
ImmutableSet
インスタンスに要素を集めるためのCollectorの例を書きましょう。正しい型を指定することから始めます。
private class ImmutableSetCollector<T>
implements Collector<T, ImmutableSet.Builder<T>, ImmutableSet<T>> {...}
内部のコレクション操作を処理するための可変コレクションが必要なので、これには
ImmutableSet
を使用できません。他の可変コレクション、または一時的にオブジェクトを蓄積する可能性のある他のクラスを使用する必要があります。この場合は、
ImmutableSet.Builder
を使用して進み、次に5つのメソッドを実装する必要があります。
-
仕入先<ImmutableSet.Builder <T>>
仕入先
()
-
BiConsumer <ImmutableSet.Builder <T>、T>
アキュムレータ
()
-
BinaryOperator <ImmutableSet.Builder <T>>
コンバイナ
()
-
__関数<ImmutableSet.Builder <T>、ImmutableSet <T>>
-
Set <特性>
特性
()
-
The supplier()
** メソッドは空のアキュムレータインスタンスを生成する
Supplier
インスタンスを返すので、この場合は単純に次のように書くことができます。
@Override
public Supplier<ImmutableSet.Builder<T>> supplier() {
return ImmutableSet::builder;
}
-
The accumulator()
** メソッドは、既存の
accumulator
オブジェクトに新しい要素を追加するために使用される関数を返すので、
Builder
‘s
add
メソッドを使用しましょう。
@Override
public BiConsumer<ImmutableSet.Builder<T>, T> accumulator() {
return ImmutableSet.Builder::add;
}
combiner()
メソッドは、2つのアキュムレータをマージするために使用される関数を返します。
@Override
public BinaryOperator<ImmutableSet.Builder<T>> combiner() {
return (left, right) -> left.addAll(right.build());
}
finisher()
メソッドは、アキュムレータを最終結果タイプに変換するために使用される関数を返すので、この場合は
Builder
‘s
build
メソッドを使用します。
@Override
public Function<ImmutableSet.Builder<T>, ImmutableSet<T>> finisher() {
return ImmutableSet.Builder::build;
}
** ()メソッドは、内部最適化に使用される追加情報をStreamに提供するために使用されます。この場合、
Set
の要素の順序には注意を払いませんので、
Characteristics.UNORDERED
を使用します。このテーマに関する詳細情報を入手するには、
特性__ JavaDocを確認してください。
@Override public Set<Characteristics> characteristics() {
return Sets.immutableEnumSet(Characteristics.UNORDERED);
}
使い方と一緒に完全な実装は次のとおりです。
public class ImmutableSetCollector<T>
implements Collector<T, ImmutableSet.Builder<T>, ImmutableSet<T>> {
@Override
public Supplier<ImmutableSet.Builder<T>> supplier() {
return ImmutableSet::builder;
}
@Override
public BiConsumer<ImmutableSet.Builder<T>, T> accumulator() {
return ImmutableSet.Builder::add;
}
@Override
public BinaryOperator<ImmutableSet.Builder<T>> combiner() {
return (left, right) -> left.addAll(right.build());
}
@Override
public Function<ImmutableSet.Builder<T>, ImmutableSet<T>> finisher() {
return ImmutableSet.Builder::build;
}
@Override
public Set<Characteristics> characteristics() {
return Sets.immutableEnumSet(Characteristics.UNORDERED);
}
public static <T> ImmutableSetCollector<T> toImmutableSet() {
return new ImmutableSetCollector<>();
}
そしてここで行動中:
List<String> givenList = Arrays.asList("a", "bb", "ccc", "dddd");
ImmutableSet<String> result = givenList.stream()
.collect(toImmutableSet());
5結論
この記事では、Java 8の
Collectors
について詳しく調べ、その実装方法を示しました。
すべてのコード例はhttps://github.com/eugenp/tutorials/tree/master/core-java-8[GitHub]にあります。あなたはもっと面白い記事http://4comprehension.comを読むことができます[私のサイト上]。