Effiziente Methode zum Iterieren und Kopieren der Werte von HashMap

9

Ich möchte konvertieren:

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

zu:

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

inputMapwird in der Konfiguration bereitgestellt und ist bereit, aber ich muss customMapformatieren. CustomObject wird aus der List<Map<String, String>>Verwendung weniger Codezeilen in einer Funktion abgeleitet.

Ich habe versucht, die Eingabekarte auf normale Weise zu iterieren und Schlüsselwerte in customMap zu kopieren. Gibt es eine effiziente Möglichkeit, dies mit Java 8 oder einer anderen Verknüpfung zu tun?

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();
}
Geisterfahrer
quelle
Bitte formatieren Sie den Code richtig.
Akuzminykh
1
Haben Sie darüber nachgedacht, eine Fassade zu erstellen, anstatt sie zu kopieren?
ControlAltDel
Effizienter kann es nicht sein. Alle diese Operationen müssen stattfinden. Aber dieser Code funktioniert nicht wirklich. Sie fügen die Liste nicht in das benutzerdefinierte Objekt ein.
user207421

Antworten:

2

Eine Lösung besteht darin, das entrySetvon zu streamen inputMapund dann Collectors#toMapzweimal zu verwenden (einmal für das Äußere Mapund einmal für das Innere 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.
quelle
Sie können die {}und die return-Anweisung im Lambda weglassen , .collect(Collectors.toMap(Function.identity(), entry -> entry.getValue() .entrySet() .stream() .collect(Collectors.toMap(Function.identity(), entry -> getCustomeObj(entry.getValue()))); ));
ungefähr
3
@ SHoko Stimmt, aber ich denke, es würde ohne den Block weniger lesbar aussehen.
Jacob G.
1

Sie könnten streamen, aber das wird nicht lesbar aussehen. Zumindest für mich. Wenn Sie also eine Methode haben:

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

Sie könnten die java-8Syntax weiterhin verwenden , jedoch in einer anderen Form:

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

        });
    });

Wenn Sie die innere Karte erstellen können immutable, können Sie diese noch kürzer machen:

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

IMHO Streaming ist keine so schlechte Idee. Es gibt keine schlechten Werkzeuge. Es hängt davon ab, wie Sie sie verwenden.


In diesem speziellen Fall würde ich das sich wiederholende Muster in eine Dienstprogrammmethode extrahieren:

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

Die obige Methode kann mit jedem Ansatz implementiert werden, obwohl ich denke, dass sie Stream APIhier ziemlich gut passt.


Sobald Sie die Dienstprogrammmethode definiert haben, kann sie wie folgt verwendet werden:

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

Die eigentliche Transformation ist effektiv ein Liner. So mit der richtigen JavaDocfür transformValuesMethode ist das Ergebnis Code ziemlich lesbar und wartbar.

ETO
quelle
1

Wie wäre es mit Collectors.toMapden Einträgen sowohl auf äußerer als auch auf innerer Ebene wie:

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
quelle