简体   繁体   中英

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

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.

This does not look pretty but uses streams and converts to Map<ENUM_A, Map<ENUM_A, List<ENUM_B>>> using EnumMap for the each Map.

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>>> out2 = in.entrySet().stream()
    .map(e -> Map.entry(ENUM_A.valueOf(e.getKey()),
                        e.getValue().entrySet().stream()
                         .map(x -> Map.entry(ENUM_A.valueOf(x.getKey()),
                                                            x.getValue().stream().map(ENUM_B::valueOf).collect(Collectors.toList())))
                         .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b, () -> new EnumMap<>(ENUM_A.class)))))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b, () -> new EnumMap<>(ENUM_A.class)));

System.out.println("out2="+out2);

=>

out2={APPLE={APPLE=[RADDISH], BANANA=[ONION, RADDISH]}, BANANA={APPLE=[ONION, RADDISH], BANANA=[ONION]}}

It looks a bit neater if you revert to the default toMap() handling by deleting the , (a, b) -> b, () -> new EnumMap<>(ENUM_A.class) from within each toMap call.

EDIT

However I find it easier to define 2 simple utility methods which can be added to any helper class to transform Map<K,V> to Map<X,Y> and List<X> to List<Y> :

/**
 * Utility to convert List<X> to List<Y> by applying mapper function
 */
static <X,Y> List<Y> toList(List<X> list, Function<X,Y> valueMapper) {
    return list.stream().map(valueMapper).collect(Collectors.toList());
}

/**
 * Utility to convert Map<K,V> to Map<X,Y> by applying mapper functions
 */
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(k -> keyMapper.apply(k.getKey()), v -> valueMapper.apply(v.getValue())));
}

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM