Home > Blockchain >  How to convert Stream of Maps into a single Map
How to convert Stream of Maps into a single Map

Time:03-12

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]}
  • Related