入れ子構造内の複数の要素のための「groupingBy」を使用してマップを収集する方法はありますか?

GhostCat敬礼モニカC.:

まず、コンテキストコードのビット:

import java.util.*;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.function.Function;
import java.util.stream.Collectors;

class Scratch {

  static enum Id {A, B, C}
  static class IdWrapper {
    private final Id id;
    public IdWrapper(Id id) {this.id = id;}
    Id getId() { return id; }
  }

  public static void main(String[] args) {
    Map<String, Object> v1 = new HashMap<>();
    v1.put("parents", new HashSet<>(Arrays.asList(new IdWrapper(Id.A), new IdWrapper(Id.B))));
    v1.put("size", 1d);

    Map<String, Object> v2 = new HashMap<>();
    v2.put("parents", new HashSet<>(Arrays.asList(new IdWrapper(Id.B), new IdWrapper(Id.C))));
    v2.put("size", 2d);

    Map<String, Map<String, Object>> allVs = new HashMap<>();
    allVs.put("v1", v1);
    allVs.put("v2", v2);

上記の私が扱っていたデータ構造を表しています。Iの値として、内側「プロパティマップ」を含むアウターマップ(キータイプは無関係である)を有します。これらの内部のマップには、データの異なる種類を検索する文字列を使用しています。

私が働いている場合、それぞれV1、V2で、...「ディスク」を表しています。各ディスクには、特定のサイズを持っていますが、複数の親を持つことができます。

今、私はサイズ合計する必要があるごとに、親IDをMap<Id, Double>上記の例では、そのマップは、あろう{B=3.0, A=1.0, C=2.0}

次のコードは、期待される結果を与えます:

    HashMap<Id, DoubleAdder> adders = new HashMap<>();
    allVs.values().forEach(m -> {
        double size = (Double) m.get("size");
        Set<IdWrapper> wrappedIds = (Set<IdWrapper>) m.get("parents");
        wrappedIds.forEach(w -> adders.computeIfAbsent(w.getId(), a -> new DoubleAdder()).add(size));
    });

    System.out.println(adders.keySet().stream()
            .collect(Collectors.toMap(Function.identity(), key -> adders.get(key).doubleValue())));

しかし、コードは(私はサイズを追加するための第2のマップを必要とするという事実のように)かなり不格好を感じています。

私は常に1人の親が似たようなケースを、持っている、それは簡単に使用して解決することができます

collect(Collectors.groupingBy(...), Collectors.summingDouble(...);

しかし、私は、「複数」の親の場合のために失われています。

だから、質問:上記の変換が必要と計算することができますMap<Id, Double>使用して書き換えることgroupingBy()

そして、ちょうどレコードの:上記は、私はのための答えを必要とする問題のためだけMCVEです。私は、「データレイアウト」は奇妙に見えるかもしれないことを理解しています。実際には、我々は実際には例えばこれらの「ディスク」を表す別個のクラスを持っています。しかし、私たちの「枠組み」はまた、IDとプロパティ名を使用して、データベース内の任意のオブジェクトのプロパティにアクセスすることができます。あなたはパフォーマンスの問題を持っている場合、時には、そのようなAの「生のプロパティマップ」の方法をデータをフェッチすると、オブジェクト自体を真の「ディスク」へのアクセスに比べて桁違いに高速です。言い換えれば、私は、コンテキストについては何も変更することはできません。私の質問は、その計算を書き換えについてのみです。

(私はJava8に拘束していますし、「標準」のJavaライブラリが、追加の新しいバージョンのJavaまたはこれを解決するための素敵な非標準的な方法のための答えは、あまりにも、理解されるであろう)

彼らは次のとおりでした:

ここでは、単一のストリームパイプラインのソリューションです:

Map<Id,Double> sums = allVs.values ()
                           .stream () 
                           .flatMap (m -> ((Set<IdWrapper>)m.get ("parents")).stream ()
                                                                             .map (i -> new SimpleEntry<Id,Double>(i.getId(),(Double)m.get ("size"))))
                           .collect (Collectors.groupingBy (Map.Entry::getKey,
                                                            Collectors.summingDouble (Map.Entry::getValue)));

出力:

{B=3.0, A=1.0, C=2.0}

アイデアは、それぞれの内側に変換することMapStreamキーがあるエントリのId(「親」のSet)値は、対応する「サイズ」です。

そして、それはグループに簡単だStream所望の出力に。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=171743&siteId=1