\$\begingroup\$
\$\endgroup\$
1
I am trying to merge multiple map with Java stream API. The reqiurements like ...
- this method can receive multiple map, does not care hot many of it
- support 2 merge method: intersection, union
- if key duplicates, it overwrites with later value
Here is basic code I wrote,
public class MapMergeHelper {
public static Map<String, String> merge (MapMergeOperation mergeOperation, Map<String, String> ... maps) {
Map<String, String> merged;
switch (mergeOperation) {
case INTERSECTION -> {
Set<Map.Entry<String, String>> entrySet = maps[0].entrySet();
for (int i = 1; i < maps.length; i++) {
final int current = i;
entrySet = entrySet.stream().filter(entry -> maps[current].containsKey(entry.getKey()))
.collect(Collectors.toSet());
}
merged = entrySet.stream()
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
}
default -> {
// UNION -> If key duplicates, then override
merged = Stream.of(maps)
.flatMap(entry -> entry.entrySet().stream())
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue(), (o, o2) -> o2));
}
}
return merged;
}
}
MapMergeOperation
is simple Enum like this
public enum MapMergeOperation {
UNION,
INTERSECTION
}
And here is the testcase
public class MapMergeHelperTest {
@Test
public void testMerge() {
Map<String, String> testEntryMapA = Map.of("key1", "a", "key2", "b", "key3", "c", "key4", "d");
Map<String, String> testEntryMapB = Map.of("key1", "b");
Map<String, String> testEntryMapC = Map.of("key1", "a", "key5", "e");
{
// test intersection
Map<String, String> actual = MapMergeHelper.merge(MapMergeOperation.INTERSECTION, testEntryMapA, testEntryMapB, testEntryMapC);
assertThat(actual.keySet()).containsExactly("key1");
assertThat(actual.get("key1")).isEqualTo("a"); // override
}
{
// test union
Map<String, String> actual = MapMergeHelper.merge(MapMergeOperation.UNION, testEntryMapA, testEntryMapB, testEntryMapC);
assertThat(actual.keySet()).containsExactlyInAnyOrder("key1", "key2", "key3", "key4", "key5");
}
}
}
The problem is, I think it is not efficient enough because calling collect(Collections.toSet())
every time inside of for loop.
I am looking for a way to achieve goal in better readable, efficient way!
asked Feb 9, 2023 at 7:56
1 Answer 1
\$\begingroup\$
\$\endgroup\$
Some basic tips:
- Split this into 2 methods, drop the
enum
. It doesn't do you any good unless something is really forcing you to have it there. It will be a lot more readable and easier to have 2 separate functionsintersect
andunion
. Then you can worry about them separately, make separate tests for each of those as individual parts. - Don't mix streams and for loops. Choose one and stick with it. This makes the code confusing and less readable.
- No point of this line
final int current = i;
, just usei
directly or renamei
tocurrent
. - Collecting to set is fine, but your logic could be improved. Take at look at Set.retainAll
- Variable
merged
is unnecessary, you always assign it only to return in the next line. Just return the result already.
lang-java
map[0].keySet().retainAll(map[i].keySet());
Loop in reverse order to retain the value in last map. \$\endgroup\$