開発者ドキュメント

Java 8のコレクターガイド


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> {...}

  1. T

    – 収集に利用できるオブジェクトの種類


  2. A

    – 可変アキュムレータオブジェクトの型


  3. 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を読むことができます[私のサイト上]。

モバイルバージョンを終了