Manera eficiente de iterar y copiar los valores de HashMap

9

Quiero convertir:

Map<String, Map<String, List<Map<String, String>>>> inputMap 

a:

Map<String, Map<String, CustomObject>> customMap

inputMapse proporciona en la configuración y está listo, pero necesito customMapformatear. CustomObject se derivará del List<Map<String, String>>uso de pocas líneas de código en una función.

He intentado una forma normal de iterar el mapa de entrada y copiar valores clave en customMap. ¿Hay alguna manera eficiente de hacerlo usando Java 8 o algún otro atajo?

Map<String, Map<String, List<Map<String, String>>>> configuredMap = new HashMap<>();
Map<String, Map<String, CustomObj>> finalMap = new HashMap<>();


for (Map.Entry<String, Map<String, List<Map<String, String>>>> attributeEntry : configuredMap.entrySet()) {
    Map<String, CustomObj> innerMap = new HashMap<>();
    for (Map.Entry<String, List<Map<String, String>>> valueEntry : attributeEntry.getValue().entrySet()) {
        innerMap.put(valueEntry.getKey(), getCustomeObj(valueEntry.getValue()));
    }
    finalMap.put(attributeEntry.getKey(), innerMap);
}

private CustomObj getCustomeObj(List<Map<String, String>> list) {
    return new CustomObj();
}
jinete Fantasma
fuente
Por favor, formatee el código correctamente.
akuzminykh
1
¿Has pensado en crear una fachada, en lugar de copiar?
ControlAltDel
No puede haber una forma más eficiente. Todas esas operaciones tienen que llevarse a cabo. Pero este código en realidad no funciona. No está poniendo la lista en el objeto personalizado.
user207421

Respuestas:

2

Una solución es transmitir el entrySetde inputMap, y luego usar Collectors#toMapdos veces (una para el exterior Mapy otra para el interior Map):

Map<String, Map<String, CustomObj>> customMap = inputMap.entrySet()
        .stream()
        .collect(Collectors.toMap(Function.identity(), entry -> {
            return entry.getValue()
                        .entrySet()
                        .stream()
                        .collect(Collectors.toMap(Function.identity(), 
                            entry -> getCustomeObj(entry.getValue())));
        }));
Jacob G.
fuente
Puede omitir el {}y la declaración de devolución en la lambda, algo como esto:.collect(Collectors.toMap(Function.identity(), entry -> entry.getValue() .entrySet() .stream() .collect(Collectors.toMap(Function.identity(), entry -> getCustomeObj(entry.getValue()))); ));
SHoko
3
@SHoko True, pero creo que se vería menos legible sin el bloque.
Jacob G.
1

Podría transmitir, pero eso no se verá legible; al menos para mi Entonces, si tienes un método:

static CustomObject fun(List<Map<String, String>> in) {
    return .... // whatever processing you have here
}

aún podría usar la java-8sintaxis, pero en una forma diferente:

    Map<String, Map<String, CustomObject>> customMap = new HashMap<>();

    inputMap.forEach((key, value) -> {

        value.forEach((innerKey, listOfMaps) -> {

            Map<String, CustomObject> innerMap = new HashMap<>();
            innerMap.put(innerKey, fun(listOfMaps));
            customMap.put(key, innerMap);

        });
    });

Si puede hacer el mapa interno immutable, podría hacerlo aún más corto:

inputMap.forEach((key, value) -> {
      value.forEach((innerKey, listOfMaps) -> {
          customMap.put(key, Collections.singletonMap(innerKey, fun(listOfMaps)));
      });
});
Eugene
fuente
1

La transmisión en mi humilde opinión no es tan mala idea. No hay malas herramientas. Depende de cómo los estés usando.


En este caso particular, extraería el patrón repetitivo en un método de utilidad:

public static <K, V1, V2> Map<K, V2> transformValues(Map<K, V1> map, Function<V1, V2> transformer) {
    return map.entrySet()
              .stream()
              .collect(toMap(Entry::getKey, e -> transformer.apply(e.getValue())));
}

El método anterior se puede implementar utilizando cualquier enfoque, aunque creo que Stream APIencaja bastante bien aquí.


Una vez que haya definido el método de utilidad, puede usarse de la siguiente manera:

Map<String, Map<String, CustomObj>> customMap = 
    transformValues(inputMap, attr -> transformValues(attr, this::getCustomObj));

La transformación real es efectivamente un trazador de líneas. Así, con adecuado JavaDocpara transformValuesel método del código de resultado es bastante legible y fácil de mantener.

ETO
fuente
1

¿Qué tal Collectors.toMappara las entradas tanto a nivel externo como interno como:

Map<String, Map<String, CustomObj>> finalMap = configuredMap.entrySet()
        .stream()
        .collect(Collectors.toMap(Map.Entry::getKey,
                attributeEntry -> attributeEntry.getValue().entrySet()
                        .stream()
                        .collect(Collectors.toMap(Map.Entry::getKey,
                                valueEntry -> getCustomeObj(valueEntry.getValue())))));
Naman
fuente