Un moyen efficace pour itérer et copier les valeurs de HashMap

9

Je veux convertir:

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

à:

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

inputMapest fourni dans la config et est prêt mais j'ai besoin de customMapformater. CustomObject sera dérivé de l' List<Map<String, String>>utilisation de quelques lignes de code dans une fonction.

J'ai essayé une manière normale d'itérer la carte d'entrée et de copier les valeurs clés dans customMap. Existe-t-il un moyen efficace de le faire en utilisant Java 8 ou un autre raccourci?

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();
}
cavalier fantôme
la source
Veuillez formater le code correctement.
akuzminykh
1
Avez-vous pensé à créer une façade plutôt qu'à copier?
ControlAltDel
Il ne peut y avoir de moyen plus efficace. Toutes ces opérations doivent avoir lieu. Mais ce code ne fonctionne pas réellement. Vous ne mettez pas la liste dans l'objet personnalisé.
user207421

Réponses:

2

Une solution consiste à diffuser le entrySetde inputMap, puis à utiliser Collectors#toMapdeux fois (une pour l'extérieur Mapet une pour l'intérieur 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.
la source
Vous pouvez omettre la {}et la déclaration de retour dans la lambda, quelque chose comme ceci:.collect(Collectors.toMap(Function.identity(), entry -> entry.getValue() .entrySet() .stream() .collect(Collectors.toMap(Function.identity(), entry -> getCustomeObj(entry.getValue()))); ));
SHoko
3
@SHoko Vrai, mais je pense que cela aurait l'air moins lisible sans le bloc.
Jacob G.
1

Vous pouvez diffuser, mais cela ne semblera pas lisible; au moins pour moi. Donc, si vous avez une méthode:

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

vous pouvez toujours utiliser la java-8syntaxe, mais sous une forme différente:

    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 vous pouvez faire la carte intérieure immutable, vous pouvez la raccourcir encore plus:

inputMap.forEach((key, value) -> {
      value.forEach((innerKey, listOfMaps) -> {
          customMap.put(key, Collections.singletonMap(innerKey, fun(listOfMaps)));
      });
});
Eugène
la source
1

Le streaming à mon humble avis n'est pas une si mauvaise idée. Il n'y a pas de mauvais outils. Cela dépend de la façon dont vous les utilisez.


Dans ce cas particulier, j'extrais le motif répétitif dans une méthode utilitaire:

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

La méthode ci-dessus peut être implémentée en utilisant n'importe quelle approche, bien que je pense qu'elle Stream APIconvient assez bien ici.


Une fois que vous avez défini la méthode utilitaire, elle peut être utilisée de la manière suivante:

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

La transformation réelle est en fait un revêtement. Ainsi, avec la méthode appropriée JavaDocpour transformValuesle code de résultat est assez lisible et maintenable.

ETO
la source
1

Que diriez-vous Collectors.toMappour les entrées à la fois à un niveau extérieur et intérieur tels que:

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
la source