Эффективный способ перебора и копирования значений HashMap - PullRequest
9 голосов
/ 13 марта 2020

Я хочу преобразовать:

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

в:

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

inputMap предоставляется в конфигурации и готов, но мне нужно customMap Форматировать. CustomObject будет получен из List<Map<String, String>> с использованием нескольких строк кода в функции.

Я пробовал нормальный способ итерации входной карты и копирования значений ключей в customMap. Есть ли эффективный способ сделать это, используя Java 8 или другой ярлык?

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

Ответы [ 4 ]

2 голосов
/ 13 марта 2020

Одним из решений является потоковая передача entrySet из inputMap, а затем использование Collectors#toMap дважды (один раз для внешнего Map и один раз для внутреннего 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())));
        }));
1 голос
/ 14 марта 2020

Как насчет Collectors.toMap для записей как на внешнем, так и на внутреннем уровне, таких как:

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())))));
1 голос
/ 13 марта 2020

ИМХО потоковое не так уж плохая идея. Там нет плохих инструментов. Это зависит от того, как вы их используете.


В этом конкретном случае я бы выделил повторяющийся шаблон в служебный метод:

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

Вышеприведенный метод может быть реализован с использованием любой подход, хотя я думаю, что Stream API вполне подходит здесь.


После того, как вы определили метод утилит, его можно использовать так просто, как указано ниже:

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

Фактический трансформация фактически один лайнер. Таким образом, при правильном методе JavaDoc для transformValues код результата может быть легко читаем и обслуживаем.

1 голос
/ 13 марта 2020

Вы можете транслировать, но это не будет выглядеть читабельно; по крайней мере для меня. Поэтому, если у вас есть метод:

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

, вы все равно можете использовать синтаксис java-8, но в другой форме:

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

        });
    });

Если вы можете сделать внутреннюю карту immutable Вы можете сделать это еще короче:

inputMap.forEach((key, value) -> {
      value.forEach((innerKey, listOfMaps) -> {
          customMap.put(key, Collections.singletonMap(innerKey, fun(listOfMaps)));
      });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...