2017-01-05 8 views
0

使用java8に地図<文字列、地図<文字列、設定 >>ある構造2つのマップをマージm1、m2では、mergedMapを変更します。例えば java8は二つのマップをマージする任意の簡潔な構文があり、簡潔な

{ 
    "k1": { 
     "v11": [ 
      11 
     ] 
    }, 
    "k2": { 
     "v2": [ 
      21, 
      22 
     ] 
    }, 
    "k3": { 
     "v3": [ 
      31 
     ] 
    } 
} 

私が欲しいものをマージされたマップがマージされている:

{ 
    "k1": { 
     "v1": [ 
      11, 
      12 
     ] 
    }, 
    "k2": { 
     "v2": [ 
      21 
     ] 
    } 
} 

平方メートルのように3つの要素が含まれています

m1がよう2つの要素が含まれています3要素。

特に、 "k1"の値はm1とm2の間で結合します。

{ 
    "k1": { 
     "v1": [ 
      11, 
      12 
     ], 
     "v11": [ 
      11 
     ] 
    }, 
    "k2": { 
     "v2": [ 
      21, 
      22 
     ] 
    }, 
    "k3": { 
     "v3": [ 
      31 
     ] 
    } 
} 

そして、私はキーが< "K3" でマージされた地図ではいくつかの要素を追加し、< "V3"、??? >>。

原点マップm2の要素がそれ以上修正されないようにしたいと思います。

+2

ストリームが役立つかもしれませんが、明確ではありませんが、正確に結果になりたいものは何ですか? –

+0

私はあなたの返信のために私の質問に例を追加しました! –

+1

um ... 'flatMap'? – emotionlessbananas

答えて

1

ストリームはので、ここでは、すべての置き換えを意味するもので私の見解ではありませんした。

for (Map.Entry<String, Map<String, Set<Long>>> entry : m2.entrySet()) { 
     m1.merge(entry.getKey(), entry.getValue(), (v1, v2) -> { 
      for (Map.Entry<String, Set<Long>> e : v2.entrySet()) { 
       v1.merge(e.getKey(), e.getValue(), (s1, s2) -> { 
        s1.addAll(s2); 
        return s1; 
       }); 
      } 
      return v1; 
     }); 
    } 

または他の提案の答えのようにflatMapを使用しました:あなたの入力を持つ

  Stream.of(m1, m2) 
      .flatMap(map -> map.entrySet().stream()) 
      .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> { 
       for (Map.Entry<String, Set<Long>> e : v2.entrySet()) { 
        v1.merge(e.getKey(), e.getValue(), (s1, s2) -> { 
         s1.addAll(s2); 
         return s1; 
        }); 
       } 
       return v1; 
      }, HashMap::new)); 

[k1={v1=[11, 12]}, k2={v2=[21]}] 
{k1={v11=[11]}, k2={v2=[21, 22]}, k3={v3=[31]}} 

出力は次のようになります。

ネストされたコレクションをマージ
{k1={v11=[11], v1=[11, 12]}, k2={v2=[21, 22]}, k3={v3=[31]}} 
+1

これは、ソースデータを変更します。 – Holger

+0

@Holgerは、入力を変更しないという部分に気づいていませんでした。私の悪い – Eugene

2

種類:

HashMap<String, Map<String, Set<String>>> m1 = new HashMap<>(); 
HashMap<String, Map<String, Set<String>>> m2 = new HashMap<>(); 

Stream.of(m1,m2).flatMap(m -> m.entrySet().stream()) 
     // stream of entries <String,Set<String> 
     .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (s1, s2) -> { /* do whatever you like to merge your nested maps */ return new HashMap<>()} ) 

`

そしてもちろん、あなたは、ネストされたマップをマージする必要があります。彼らがいたので、これは、元のデータ構造を残す

4

ソースコレクションを変更することなく、それは一見見えるかもしれませんよりも複雑です。インプレースマージを実行してソースマップの1つを変更する場合、ネストされたアプリケーションMap.mergeが実行されますが、ネストされたデータを新しいコンテナに収集するには、Java 9と同様のコンストラクタが必要です。flatMapping collectorthis answerの最後を参照)flatMappingコレクタのバックポートを使用している場合これはまた、Java 8で動作

Map<String, Map<String, Set<Long>>> m3 = 
    Stream.concat(m1.entrySet().stream(), m2.entrySet().stream()) 
    .collect(groupingBy(Map.Entry::getKey, 
     flatMapping(e -> e.getValue().entrySet().stream(), 
     groupingBy(Map.Entry::getKey, flatMapping(e -> e.getValue().stream(), toSet()))))); 

:このコレクタでは、解決策は次のようになります。

Map<String, Map<String, Set<Long>>> m3 = 
    Stream.of(m1, m2).collect(mergeMaps(mergeMaps(mergeSets()))); 

static <T> Collector<Set<T>,?,Set<T>> mergeSets() { 
    return Collector.of(HashSet::new, Set::addAll, (s1,s2)->{ s1.addAll(s2); return s1; }); 
} 
static <K,V> Collector<Map<K,V>,?,Map<K,V>> mergeMaps(Collector<V,?,V> c) { 
    return collectingAndThen(reducing(
     (m1,m2) -> Stream.concat(m1.entrySet().stream(), m2.entrySet().stream()) 
      .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, c)))), 
     o -> o.orElse(Collections.emptyMap())); 
} 

これらのコレクターは仕事をするために組み合わせることができます

代替はマージするMapの数がかなり少ないことを考慮すると、コレクターはこのユースケースに特化した作成することです。

+0

素晴らしい答えが、あなたの最後の解決策はあまりにも病気です。もしできれば+2。 – Eugene

関連する問題