Johno
Johno

Reputation: 162

Merge two Maps of type Map<String, Set<String>>

I have two maps of type Map<String, Set<String>> that I need to merge based on the key of each. I'm using Java 8.

I have tried the following:

Map<String, Set<String>> collect = Stream.of(keyColumnValueAccumulator, keyColumnValues)
                .map(Map::entrySet)
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(
                        Map.Entry::getKey, 
                        Map.Entry::getValue));

But at runtime this results in a 'Duplicate Key' error. Please can someone help? I'm still quite new to Java 8 and the Stream API

Upvotes: 2

Views: 2447

Answers (2)

Ousmane D.
Ousmane D.

Reputation: 56423

You could also use the Map::merge method to avoid the duplicate key error.

Example:

Map<String, Set<String>> accumulator = new HashMap<>();
firstMap.forEach((k, v) -> accumulator.merge(k, v, (prev, cur) -> {prev.addAll(cur); return prev;}));
secondMap.forEach((k, v) -> accumulator.merge(k, v, (prev, cur) -> {prev.addAll(cur); return prev;}));

In the case of a key collision, we simply accumulate the two sets into one, but if this is not the desired behaviour you can always alter it or create a custom function for the remappingFunction.

Upvotes: 3

Ousmane D.
Ousmane D.

Reputation: 56423

Use the merge function to decide what to do in case of a key collision:

for example, you could merge the two sets into one then return the new merged result:

 Stream.of(keyColumnValueAccumulator, keyColumnValues)
       .map(Map::entrySet)
       .flatMap(Collection::stream)
       .collect(Collectors.toMap(
             Map.Entry::getKey,
             Map.Entry::getValue,
            (left, right) -> {left.addAll(right); return left;}));

or you can keep the first and discard the second:

 Stream.of(keyColumnValueAccumulator, keyColumnValues)
           .map(Map::entrySet)
           .flatMap(Collection::stream)
           .collect(Collectors.toMap(
                 Map.Entry::getKey,
                 Map.Entry::getValue,
                (left, right) -> left);

or the reciprocal:

Stream.of(keyColumnValueAccumulator, keyColumnValues)
           .map(Map::entrySet)
           .flatMap(Collection::stream)
           .collect(Collectors.toMap(
                 Map.Entry::getKey,
                 Map.Entry::getValue,
                (left, right) -> right);

or call another function to compute the new value and so forth...

Upvotes: 2

Related Questions