Java Streamのcollect()メソッドの例

Java Streamのcollect()は、ストリームの要素に対して可変の集約操作を実行します。この操作は終端操作です。

可変削減操作とは何ですか? (Kahen saku gen sōsa to wa nanidesu ka?)

可変削減操作は、ストリーム要素を処理し、それを可変の結果コンテナに蓄積します。要素が処理されると、結果コンテナを結合する結合関数が存在し、それによって結果が作成されます。

JavaのStream collect()メソッドのシグネチャを日本語で書き換えると、以下のようになります。

Java Streamのcollect()メソッドには2つのバリアントがあります。

    1. R collect(Supplier supplier, BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner) can be paraphrased as:

 

    R collect(Supplier supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)を別の言い方にすると、以下のようになります:

コレクターは、供給者、蓄積器、および結合器オブジェクトのためのラッパーを提供するインターフェースです。2番目のメソッドは、ビルトインのコレクター実装を提供するためにCollectorsクラスを利用する場合に便利です。collect()関数の3つのパラメータは次のとおりです。

    1. サプライヤー:新しい可変の結果コンテナを作成する機能。並列実行では、この関数は複数回呼び出される可能性があり、各呼び出しで新しい値を返さなければなりません。

 

    1. アキュムレータ:要素を結果コンテナに折りたたむ状態を持たない関数。

 

    コンバイナー:二つの部分的な結果コンテナを受け入れ、それらをマージする、アキュムレータ関数と互換性が必要な状態を持たない関数。

stream.collect()メソッドの例

Stream.collect()メソッドのいくつかの例を見てみましょう。

1. 文字列のリストを結合する

例えば、新しい文字列を作るために文字列のリストを連結したいとしましょう。私たちは、Streamのcollect()関数を使用して、可変の縮小操作を行い、リストの要素を連結することができます。

List<String> vowels = List.of("a", "e", "i", "o", "u");

// sequential stream - nothing to combine
StringBuilder result = vowels.stream().collect(StringBuilder::new, (x, y) -> x.append(y),
		(a, b) -> a.append(",").append(b));
System.out.println(result.toString());

// parallel stream - combiner is combining partial results
StringBuilder result1 = vowels.parallelStream().collect(StringBuilder::new, (x, y) -> x.append(y),
		(a, b) -> a.append(",").append(b));
System.out.println(result1.toString());

結果: 出力

aeiou
a,e,i,o,u
  • The supplier function is returning a new StringBuilder object in every call.
  • The accumulator function is appending the list string element to the StringBuilder instance.
  • The combiner function is merging the StringBuilder instances. The instances are merged with each other with a comma between them.
  • In the first case, we have a sequential stream of elements. So they are processed one by one and there is only one instance of StringBuilder. There is no use of the combiner function. That’s why the output produced is “aeiou”.
  • In the second case, we have a parallel stream of strings. So, the elements are processed parallelly and there are multiple instances of StringBuilder that are being merged by the combiner function. Hence, the output produced is “a,e,i,o,u”.
  • If the stream source is ordered such as List, the collect() method maintains the order while processing. If the stream source is unordered such as Set, then the collect() method can produce different results in each invocation.

文字列のリストを連結したい場合、コードサイズを削減するためにメソッド参照を使用することができます。

String result2 = vowels.parallelStream()
		.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
		.toString();

2. Collectorsクラスを使用して、Stream collect()をListに集める。

Collectorsクラスは、Collectorインターフェースの多くの便利な実装を提供します。整数のリストから偶数のみを選択する例を見てみましょう。Streamのfilter()は中間操作であり、ストリームを返します。したがって、このストリームからリストを作成するためにcollect()関数を使用します。

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
		
List<Integer> evenNumbers = numbers.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
System.out.println(evenNumbers);  // [2, 4, 6]

Collectors.toList()は、入力要素を新しいリストに蓄積するCollectorの実装を返します。

3. ストリームをcollect()メソッドでSetに変換する

私たちは、Collectors.toSet()を使用して、ストリームの要素を新しいSetに収集することができます。

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);

Set<Integer> oddNumbers = numbers.parallelStream().filter(x -> x % 2 != 0).collect(Collectors.toSet());
System.out.println(oddNumbers); // [1, 3, 5]

3. ストリームのcollect()メソッドを使ってMapに変換する。

ストリームの要素をMapに集約するために、Collectors.toMap()関数を使用することができます。このメソッドは、Map内でのキーと対応する値のマッピングに対して2つの引数を受け入れます。

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);

Map<Integer, String> mapOddNumbers = numbers.parallelStream().filter(x -> x % 2 != 0)
		.collect(Collectors.toMap(Function.identity(), x -> String.valueOf(x)));
System.out.println(mapOddNumbers); // {1=1, 3=3, 5=5}

4. コレクターが参加する()例

私たちは、Collectors joining() メソッドを使用することで、入力ストリームのCharSequence要素を遭遇した順に連結するCollectorを取得することができます。これを使用して、文字列、StringBuffer、またはStringBuilderのストリームを連結することができます。

jshell> String value = Stream.of("a", "b", "c").collect(Collectors.joining());
value ==> "abc"

jshell> String valueCSV = Stream.of("a", "b", "c").collect(Collectors.joining(","));
valueCSV ==> "a,b,c"

jshell> String valueCSVLikeArray = Stream.of("a", "b", "c").collect(Collectors.joining(",", "{", "}"));
valueCSVLikeArray ==> "{a,b,c}"

jshell> String valueObject = Stream.of("1", new StringBuffer("2"), new StringBuilder("3")).collect(Collectors.joining());
valueObject ==> "123"

出力:

Java Stream Collect Example

結論

JavaのStreamのcollect()は、主にストリームの要素をコレクションに集めるために使用されます。これは終端操作です。並列ストリームと一緒に使用する際には、同期処理を担当します。Collectorsクラスは、私たちを助けるために多くのCollector実装を提供します。

リファレンス

  • Stream collect() API Doc
  • Collector Interface
  • Collectors API Doc
コメントを残す 0

Your email address will not be published. Required fields are marked *