0

I have a map read from my static config in the following format: Map<String, Map<String, List<String>>> dependentPluginEntityMapString. The string values in this map are actually from ENUMs and the correct and required representation of the map is Map<ENUM_A, Map<ENUM_A, List<ENUM_B>>>.

ENUM_A {
 APPLE, BANANA
}

ENUM_B {
ONION, RADDISH
}
  1. How can I convert the map of strings to the one with enums Map<ENUM_A, Map<ENUM_A, List<ENUM_B>>> for more type safety?

I know, I can iterate on the string map (using for or streams) and create a new map with enums as required but looking for a better/more efficient and an elegant way of doing that?

This is my brute solution. Can i do better?

final Map<ENUM_A, Map<ENUM_A, List<ENUM_B>>> dependentPluginEntityMap = new HashMap<>();

        for (Map.Entry<String, Map<String, List<String>>> dependentPluginEntry:
                dependentPluginEntityMapFromAppConfig.entrySet()) {
            final Map<ENUM_A, List<ENUM_B>> independentPluginMapForEntry = new HashMap<>();

            if (MapUtils.isNotEmpty(dependentPluginEntry.getValue())) {
                for (Map.Entry<String, List<String>> independentPluginEntry:
                        dependentPluginEntry.getValue().entrySet()) {
                    if (CollectionUtils.isNotEmpty(independentPluginEntry.getValue())) {
                        independentPluginMapForEntry.put(ENUM_A.valueOf(independentPluginEntry.getKey()),
                                independentPluginEntry.getValue().stream().map(value -> ENUM_B.valueOf(value))
                                        .collect(Collectors.toList()));
                    }
                }
            }
            dependentPluginEntityMap.put(ENUM_A.valueOf(dependentPluginEntry.getKey()),
                    independentPluginMapForEntry);
        }

  1. Should I convert the map of strings to ENUMMAP , instead of map with enum keys? Will it work with my nested map structure?

Any leads apprecciated.

2
  • 1
    Your case is made complex because your enum constants are members of different types. In particular, EnumMap requires that the constants that serve as keys in the map all be members of the same enum class. It is not possible to create an EnumMap with a non-concrete type token, unfortunately. A workaround for your use case would be to make the enum types implement a common interface, and then to use the interface type as the map keys. You will not be able to use the EnumMap class in this solution,, though. Commented May 17, 2021 at 17:19
  • Okay thanks. I am also planning to use Maps only. My main question is actually the #1, on how I can convert the maps elegantly? Commented May 17, 2021 at 17:56

1 Answer 1

1

Multi-level Map conversions are easier if you define simple utility methods to manage transformation from Map<K,V> to Map<X,Y> and List<X> to List<Y>:

static <X,Y> List<Y> toList(List<X> list, Function<X,Y> valueMapper) {
    return list.stream().map(valueMapper).toList();
}

static <K,V,X,Y> Map<X, Y> toMap(Map<K, V> map, Function<K,X> keyMapper, Function<V,Y> valueMapper) {
    return map.entrySet().stream()
              .collect(Collectors.toMap(entry -> keyMapper.apply(entry.getKey()),
                                        entry -> valueMapper.apply(entry.getValue())));
}

With these definitions your transformation is reduced to applications of the above:

Map<String, Map<String, List<String>>> in = Map.of(
        "APPLE",   Map.of("APPLE", List.of("RADDISH")
                         ,"BANANA", List.of("ONION", "RADDISH"))
        ,"BANANA", Map.of("APPLE", List.of("ONION", "RADDISH")
                         , "BANANA", List.of("ONION"))
        );

Map<ENUM_A, Map<ENUM_A, List<ENUM_B>>> map  
    = toMap(in, ENUM_A::valueOf,
                m -> toMap(m, ENUM_A::valueOf, 
                              list -> toList(list, ENUM_B::valueOf)));

=>
map ={BANANA={BANANA=[ONION]
             , APPLE=[ONION, RADDISH]}
    , APPLE={BANANA=[ONION, RADDISH]
             , APPLE=[RADDISH]}}
Sign up to request clarification or add additional context in comments.

3 Comments

is using streams better, always? I would want to have a more cleaner code with almost same performance as these map sizes will not be large for sure. Hence, clean and simple is what I am aiming.
Can I go more cleaner impl than what I have added, using streams?
@user2696258 the answer is now refactored into simpler components to avoid duplicating the same code to deal with both maps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.