Converting a map of string key-values to enum key-values in Java

1.2k views Asked by At

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.

1

There are 1 answers

3
DuncG On

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