Skuteczny sposób na iterację i kopiowanie wartości HashMap

9

Chcę przekonwertować:

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

do:

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

inputMapjest podany w konfiguracji i jest gotowy, ale muszę customMapsformatować. CustomObject zostanie wyprowadzony z List<Map<String, String>>użycia kilku wierszy kodu w funkcji.

Próbowałem normalnego sposobu iteracji mapy wejściowej i kopiowania kluczowych wartości w customMap. Czy jest jakiś skuteczny sposób, aby to zrobić za pomocą Java 8 lub innego skrótu?

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();
}
ghostrider
źródło
Sformatuj kod poprawnie.
akuzminykh
1
Czy myślałeś o stworzeniu fasady, a nie o kopiowaniu?
ControlAltDel
Nie może być bardziej efektywnego sposobu. Wszystkie te operacje muszą mieć miejsce. Ale ten kod nie działa. Nie wstawiasz listy do obiektu niestandardowego.
user207421

Odpowiedzi:

2

Jednym z rozwiązań jest strumieniowo entrySetz inputMap, a następnie za pomocą Collectors#toMappodwójnie (raz na zewnętrznej Map, a raz na wewnętrzny 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.
źródło
Możesz pominąć {}wyrażenie i zwrot w lambda, coś takiego:.collect(Collectors.toMap(Function.identity(), entry -> entry.getValue() .entrySet() .stream() .collect(Collectors.toMap(Function.identity(), entry -> getCustomeObj(entry.getValue()))); ));
SHoko
3
@SHoko Prawda, ale myślę, że bez bloku wyglądałby mniej czytelnie.
Jacob G.
1

Możesz przesyłać strumieniowo, ale nie będzie to czytelne; przynajmniej dla mnie. Więc jeśli masz metodę:

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

nadal możesz użyć java-8składni, ale w innej formie:

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

        });
    });

Jeśli możesz zrobić wewnętrzną mapę immutable, możesz ją jeszcze skrócić:

inputMap.forEach((key, value) -> {
      value.forEach((innerKey, listOfMaps) -> {
          customMap.put(key, Collections.singletonMap(innerKey, fun(listOfMaps)));
      });
});
Eugene
źródło
1

Streaming IMHO nie jest taki zły. Nie ma złych narzędzi. To zależy od tego, jak ich używasz.


W tym konkretnym przypadku wyodrębnię powtarzalny wzór do metody użyteczności:

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

Powyższą metodę można wdrożyć przy użyciu dowolnego podejścia, choć myślę, że Stream APIpasuje tutaj całkiem dobrze.


Po zdefiniowaniu metody użyteczności można jej użyć w następujący sposób:

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

Rzeczywista transformacja to w rzeczywistości jeden liniowiec. Więc z właściwą JavaDocdla transformValuessposobu kod wynik jest dość czytelne i utrzymaniu.

ETO
źródło
1

Co powiesz Collectors.toMapna wpisy zarówno na poziomie zewnętrznym, jak i wewnętrznym, takie jak:

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
źródło