配列のマップが2つあります。
Map<String, List<String>> map1 = new HashMap<>();
Map<String, List<String>> map2 = new HashMap<>();
それらを1つの新しいマップにマージします。
両方のマップにキーが存在する場合、その場合、配列をマージする必要があります。
例えば:
map1.put("k1", Arrays.asList("a0", "a1"));
map1.put("k2", Arrays.asList("b0", "b1"));
map2.put("k2", Arrays.asList("z1", "z2"));
// Expected output is
Map 3: {k1=[a0, a1], k2=[b0, b1, z1, z2]}
ストリームでそれをやろうとしました
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().stream().collect(Collectors.toList())
));
これは、マップに同じキーがない場合に機能します。そうでなければ、例外が発生します
Exception in thread "main" Java.lang.IllegalStateException: Duplicate key k2 (attempted merging values [b0, b1] and [z1, z2])
at Java.base/Java.util.stream.Collectors.duplicateKeyException(Collectors.Java:133)
at Java.base/Java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.Java:180)
at Java.base/Java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.Java:169)
at Java.base/Java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.Java:1751)
at Java.base/Java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.Java:658)
at Java.base/Java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.Java:274)
at Java.base/Java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.Java:948)
at Java.base/Java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.Java:484)
at Java.base/Java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.Java:474)
at Java.base/Java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.Java:913)
at Java.base/Java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.Java:234)
at Java.base/Java.util.stream.ReferencePipeline.collect(ReferencePipeline.Java:578)
at im.djm.Test.main(Test.Java:25)
ストリームでこのタスクを実行する方法はありますか?
または私はthrougマップを繰り返す必要がありますか?
キーが重複している場合は、マージ関数を使用します。
_Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(left, right) -> {left.addAll(right); return left;}
));
_
マージ関数で追加できる可変リストが常にあることを保証するために、e -> e.getValue().stream().collect(Collectors.toList())
をnew ArrayList<>(e.getValue())
に変更したことに注意してください。
多分。しかし、反復を使用してエントリを手動で結合することで、すべてを正しく行う可能性が高くなります。他の誰かがこのコードに取り組む必要があるかどうかはわかりませんが、読みやすいアプローチに感謝するでしょう。
次のようにすることもできます:
Map<String, List<String>> map3 = Stream.concat(map1.entrySet().stream(),
map2.entrySet().stream())
.collect(Collectors.groupingBy(Entry::getKey,
Collectors.mapping(Entry::getValue,
Collectors.flatMapping(List::stream,
Collectors.toList()))));
重複したキーをマージできるようにするオーバーロードされたtoMap()
バージョンを使用する必要があります。
toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
あなたは次のように書くことができます:
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(e1, e2) -> { e1.addAll(e2); return e1;}
));
Map<String, List<String>> map1 = new HashMap<>();
Map<String, List<String>> map2 = new HashMap<>();
map1.put("k1", Arrays.asList("a0", "a1"));
map1.put("k2", Arrays.asList("b0", "b1"));
map2.put("k2", Arrays.asList("z1", "z2"));
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(p -> p.entrySet().stream())
.flatMap(p -> p.getValue().stream().map(q -> new Pair<>(p.getKey(), q)))
.collect(
Collectors.groupingBy(
p -> p.getKey(),
Collectors.mapping(p -> p.getValue(), Collectors.toList())
)
);
これは次のように機能します。
Stream<Map<String,List<String>>>
Entry<String, List<String>>
としてPair<String, String>
ごとに1ペアにFlatMap他の方法はこのようになります。
初期化する必要がありますmap3
より大きなマップを使用します(ここでmap1
)。次に、他のマップ上でループを使用し、merge
メソッドを使用して重複キーを結合します。
Map<String, List<String>> map3 = new HashMap<>(map1);
for (Map.Entry<String, List<String>> entry : map2.entrySet()) {
List<String> values = new ArrayList<>(entry.getValue());
map3.merge(entry.getKey(),entry.getValue(),(l1, l2) -> {values.addAll(l1);
return values;
});
}
map2.forEach((key, value) -> {
List<String> values = new ArrayList<>(value);
map3.merge(key,value, (l1, l2) -> {values.addAll(l1);return values;});
});
これは、マップとリストをマージするもう1つの方法です。
_Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> Stream.concat(e1.stream(), e2.stream()).collect(Collectors.toList())
));
_
toMap
メソッドの3番目の引数は
_(e1, e2) -> Stream.concat(e1.stream(), e2.stream()).collect(Collectors.toList())
_はmergeFunction
関数です。
この機能は重複に適用されます。
マップされたキーに重複が含まれている場合(
Object.equals(Object))
に従って)、値マッピング関数が各等しい要素に適用され、提供されたマージ関数を使用して結果がマージされます。
JavaDoc
以下は、両方のマップの反復を使用した例です。最初の反復では、map1とmap2の共通のキーと値のペアを結合し、それらを結果のマップに追加するか、map1の一意のキーと値のペアを結果のマップに追加します。 2回目の反復では、map1と一致しないmap2の残りすべてを取得し、結果のマップに追加します。
public static Map<String, ArrayList<String>> joinMaps(Map<String, ArrayList<String>> map1, Map<String, ArrayList<String>> map2)
{
Map<String, ArrayList<String>> mapJoined = new HashMap<>();
//join values from map2 into values of map1 or add unique key/values of map1
for (Map.Entry<String, ArrayList<String>> entry : map1.entrySet()) {
String key = entry.getKey();
ArrayList<String> value = entry.getValue();
if(map2.containsKey(key))
{
value.addAll(map2.get(key));
mapJoined.put(key, value);
}
else
mapJoined.put(key, value);
}
//add the non-duplicates left over in map 2
for (Map.Entry<String, ArrayList<String>> entry : map2.entrySet()) {
if(!mapJoined.containsKey(entry.getKey()))
mapJoined.put(entry.getKey(), entry.getValue());
}
return mapJoined;
}
最初の反復で追加されたすべてのキーを追跡するために関数にSetを追加することもできます。そのSetのサイズ== map2のサイズであれば、マップに同じキーがあり、2番目を繰り返す必要はありません。マップ、マップ2。