Modo efficiente per iterare e copiare i valori di HashMap


9

Voglio convertire:

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

per:

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

inputMapviene fornito nella configurazione ed è pronto ma devo customMapformattarlo. CustomObject verrà derivato List<Map<String, String>>dall'uso di poche righe di codice in una funzione.

Ho provato un modo normale di iterare la mappa di input e copiare i valori chiave in customMap. Esiste un modo efficace per farlo utilizzando Java 8 o qualche altro collegamento?

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

Si prega di formattare il codice correttamente.
Akuzminykh

1
Hai pensato di creare una facciata, piuttosto che di copiare?
ControlAltDel

Non ci può essere un modo più efficiente. Tutte queste operazioni devono aver luogo. Ma questo codice in realtà non funziona. Non stai inserendo l'elenco nell'oggetto personalizzato.
user207421

Risposte:


2

Una soluzione è di eseguire lo streaming di entrySetdi inputMap, quindi utilizzare Collectors#toMapdue volte (una volta per l'esterno Mape una volta per l'interno 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())));
        }));

Puoi omettere la {}e la dichiarazione di ritorno nella lambda, qualcosa del genere:.collect(Collectors.toMap(Function.identity(), entry -> entry.getValue() .entrySet() .stream() .collect(Collectors.toMap(Function.identity(), entry -> getCustomeObj(entry.getValue()))); ));
SHoko

3
@SHoko Vero, ma penso che sembrerebbe meno leggibile senza il blocco.
Jacob G.

1

Potresti trasmettere in streaming, ma non sembrerà leggibile; almeno per me. Quindi se hai un metodo:

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

potresti ancora usare la java-8sintassi, ma in una forma diversa:

    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 riesci a creare la mappa interna immutable, potresti renderla ancora più breve:

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

1

Lo streaming IMHO non è una cattiva idea. Non ci sono strumenti cattivi. Dipende da come li stai usando.


In questo caso particolare, estrarrei il motivo ripetuto in un metodo di utilità:

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

Il metodo sopra può essere implementato usando qualsiasi approccio, anche se penso che si Stream APIadatti abbastanza bene qui.


Una volta definito il metodo di utilità, può essere utilizzato nel modo seguente:

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

La trasformazione effettiva è effettivamente una linea. Così, con una corretta JavaDocper transformValuesil metodo del codice risultato è abbastanza leggibile e mantenibile.


1

Che ne dite Collectors.toMapdi voci sia a livello esterno che interno come:

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())))));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.