Maneira eficiente de iterar e copiar os valores do HashMap

9

Eu quero converter:

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

para:

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

inputMapé fornecido na configuração e está pronto, mas eu preciso customMapformatar. O CustomObject será derivado do List<Map<String, String>>uso de poucas linhas de código em uma função.

Tentei uma maneira normal de iterar o mapa de entrada e copiar valores-chave no customMap. Existe alguma maneira eficiente de fazer isso usando o Java 8 ou algum outro atalho?

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();
}
motoqueiro Fantasma
fonte
Formate o código corretamente.
akuzminykh 13/03
11
Você já pensou em criar uma fachada, em vez de copiar?
ControlAltDel 13/03
Não pode haver maneira mais eficiente. Todas essas operações devem ocorrer. Mas esse código não funciona realmente. Você não está colocando a lista no objeto personalizado.
user207421 14/03

Respostas:

2

Uma solução é transmitir o fluxo entrySetde inputMape depois usar Collectors#toMapduas vezes (uma para o exterior Mape outra para o 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.
fonte
Você pode omitir a {}instrução e o retorno no lambda, algo como isto:.collect(Collectors.toMap(Function.identity(), entry -> entry.getValue() .entrySet() .stream() .collect(Collectors.toMap(Function.identity(), entry -> getCustomeObj(entry.getValue()))); ));
SHoko
3
@ SHoko True, mas acho que pareceria menos legível sem o bloco.
Jacob G.
1

Você pode transmitir, mas isso não parecerá legível; pelo menos para mim. Então, se você tem um método:

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

você ainda pode usar a java-8sintaxe, mas de uma 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);

        });
    });

Se você pode fazer o mapa interno immutable, pode torná-lo ainda mais curto:

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

IMHO streaming não é uma idéia tão ruim. Não há ferramentas ruins. Depende de como você os está usando.


Nesse caso em particular, eu extrairia o padrão de repetição em um método utilitário:

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())));
}

O método acima pode ser implementado usando qualquer abordagem, embora eu acho que Stream APIse encaixa muito bem aqui.


Depois de definir o método utilitário, ele pode ser usado da seguinte maneira:

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

A transformação real é efetivamente uma linha. Assim, com adequada JavaDocpara transformValueso método código de resultado é bastante legível e de fácil manutenção.

ETO
fonte
1

Que tal Collectors.toMappara as entradas nos níveis externo e 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
fonte