I need to use streams to convert the Stream<Map<EntityType, Set<String>>>
to Map<EntityType, Set<String>>
.
The EntityType
is an enum
having certain values, let's say A
, B
and C
. And there are lots of maps in the stream.
I want to concatenate all of them into one map using Stream API. Is there any way?
CodePudding user response:
You could try something like this:
public static void main(String[] args) {
Map<EntityType, Set<String>> mapA = Map.of(EntityType.A, Set.of("1", "2", "3"), EntityType.B, Set.of("4", "5", "6"));
Map<EntityType, Set<String>> mapB = Map.of(EntityType.A, Set.of("1", "4", "5"), EntityType.C, Set.of("4", "5", "6"));
Map<EntityType, Set<String>> mapC = Map.of(EntityType.A, Set.of("3", "7", "5"), EntityType.B, Set.of("4", "9", "6"));
// The stream calls you can try:
Map<EntityType, Set<String>> result = Stream.of(mapA, mapB, mapC)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(keyValuePair -> keyValuePair.getKey(),
keyValuePair -> keyValuePair.getValue(),
(value1, value2) -> combine(value1, value2)));
}
private static <T> Set<T> combine(Set<T>... sets) {
return Stream.of(sets)
.flatMap(Set::stream)
.collect(Collectors.toSet());
}
enum EntityType {
A,
B,
C
}
CodePudding user response:
For that, you can flatten the maps in the stream by applying flatMap()
. And wrap each entry with a new one. This step is required 1. to avoid mutation of the source and 2. to prevent an UnsupportedOperationException
which will be raised if sets are unmodifiable.
And then apply collect()
.
Since EntityType
is an enum
the most appropriate choice for the container provided by the supplier
will be an EnumMap
which was designed specifically for enum-keys and has a better performance than HashMap
.
Inside the combine
method merge()
is being applied on the resulting map to add entries from the stream into it.
public static <T extends Enum<T>, U> Map<T, Set<U>> collect(Stream<Map<T, Set<U>>> source,
Class<T> enumClass) {
return source
.flatMap(map -> map.entrySet().stream()
.map(entry -> Map.entry(entry.getKey(), new HashSet<>(entry.getValue()))))
.collect(() -> new EnumMap<>(enumClass),
(map, entry) -> map.merge(entry.getKey(),
entry.getValue(),
(v1, v2) -> {v1.addAll(v2); return v1;}),
Map::putAll);
}
main()
public static void main(String[] args) {
Stream<Map<EntityType, Set<String>>> source =
Stream.of(Map.of(EntityType.A, Set.of("foo"), EntityType.B, Set.of("bar")),
Map.of(EntityType.A, Set.of("foo", "bar"), EntityType.B, Set.of("bar", "fiz")),
Map.of(EntityType.A, Set.of("foobar"), EntityType.B, Set.of("abc", "www")));
System.out.println(collect(source, EntityType.class));
}
Output
{A=[bar, foobar, foo], B=[bar, abc, fiz, www]}