Collectors.toMapまたはgroupingByを使用して、マップのマップ操作の結果を収集する


8

タイプのリストがList<A>あり、マップ操作List<B>で、1つのリストにマージされたすべてのA要素のタイプの集合リストを取得します。

List<A> listofA = [A1, A2, A3, A4, A5, ...]

List<B> listofB = listofA.stream()
  .map(a -> repo.getListofB(a))
  .flatMap(Collection::stream)
  .collect(Collectors.toList());

フラットマップなし

List<List<B>> listOflistofB = listofA.stream()
  .map(a -> repo.getListofB(a))
  .collect(Collectors.toList());

タイプのマップとして結果を収集したい Map<A, List<B>>と、これまでに様々にしようとCollectors.toMapか、Collectors.groupingBy望ましい結果を得ることができたオプションはありません。


2
あなたの中に繰り返しA要素がありますList<A>か?
フェデリコペラルタシャフナー、

回答:


8

toMap制限付きメソッド参照でコレクターを使用して、必要なものを取得できます。また、このソリューションでは、ソースコンテナでAインスタンスが繰り返されていないことを前提としています。その前提条件が満たされている場合、この解決策は望ましい結果をもたらします。これがその様子です。

Map<A, Collection<B>> resultMap = listofA.stream()
    .collect(Collectors.toMap(Function.identity(), repo::getListofB);

重複するA要素がある場合は、上記の機能に加えて、このマージ機能を使用する必要があります。マージ機能は、もしあればキーの衝突を扱います。

Map<A, Collection<B>> resultMap = listofA.stream()
       .collect(Collectors.toMap(Function.identity(), repo::getListofB, 
            (a, b) -> {
                a.addAll(b);
                return a;
        }));

そしてここに、flatMapping繰り返されるA要素を処理するためにコレクターを使用する、はるかに簡潔なJava9アプローチがあります。

Map<A, List<B>> aToBmap = listofA.stream()
        .collect(Collectors.groupingBy(Function.identity(),
                Collectors.flatMapping(a -> getListofB(a).stream(), 
                        Collectors.toList())));

2
重複を処理する方法を追加しました。私はそれで答えを追加していました、それはあなたの声援、乾杯を補完すると思うので、私はそれを許します!
Federico Peralta Schaffner、

2
はい、あなたのコメントは役に立ちました。良いキャッチ!
Ravindra Ranwala、

3

それは簡単です。

listofA.stream().collect(toMap(Function.identity(), a -> getListofB(a)));

2

コレクトAに対するMapキーがされているA変わらないオブジェクト、および値は、対応するリストされているBオブジェクトを、あなたは置き換えることができtoList()、次のコレクタによって収集を

toMap(Function.identity(), a -> repo.getListOfB(a))

最初の引数は、計算する方法を定義するキー元のオブジェクトからの:identity()変わらず、ストリームの元のオブジェクトを受け取ります。

第二引数がどのように定義した値が、それはちょうどあなたのメソッドの呼び出しで構成されていので、ここでは、計算された変換AのリストにB

repoメソッドはパラメータを1つしか取らないので、ラムダをメソッド参照に置き換えることで明確さを向上させることもできます。

toMap(Function.identity(), repo::getListOfB)

@kgautronに感謝します。あなたの答えは正しく、明確に説明されています。他の回答は最初に見たので受け入れました。
Amitoj

2

この回答でAは、List<A> listofAリストの要素が繰り返された場合にどうなるかを示しています。

実際、に重複があるlistofA場合、次のコードはをスローしIllegalStateExceptionます。

Map<A, Collection<B>> resultMap = listofA.stream()
        .collect(Collectors.toMap(
                            Function.identity(), 
                            repo::getListofB);

キーに衝突がある場合(つまり、キーマッパー関数が重複を返す場合、リストに繰り返し要素がある場合など)に値Collectors.toMapマージする方法がわからないため、例外がスローされる可能性があります。Function.identity()listofA

これはドキュメントで明確に述べられています:

マップされたキーに重複が含まれている場合(によるとObject.equals(Object))、IllegalStateExceptionコレクション操作が実行されるとがスローされます。マップされたキーが重複している可能性がある場合は、toMap(Function, Function, BinaryOperator代わりに)を使用してください。

ドキュメントはまた、ソリューションを提供します。繰り返される要素がある場合、値をマージする方法を提供する必要があります。これがそのような方法の1つです。

Map<A, Collection<B>> resultMap = listofA.stream()
        .collect(Collectors.toMap(
                            Function.identity(), 
                            a -> new ArrayList<>(repo.getListofB(a)),
                            (left, right) -> {
                                left.addAll(right);
                                return left;
                            });

これは、3番目の引数としてマージ関数Collectors.toMapを受け入れるオーバーロードされたバージョンを使用します。merge関数内で、を使用して、繰り返される各要素の要素を、それぞれのunqiueリストに追加しますCollection.addAllBAAます。

値マッパー関数では、それぞれArrayListのオリジナルが変更されないように、新しいものが作成されます。また、を作成しているので、それを変更できることを事前に知っています(つまり、に重複がある場合に備えて、後でそれに要素を追加できます)。List<B>AArraylistlistofA

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.