Эффективный способ перебора и копирования значений HashMap

9

Я хочу конвертировать:

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();
}
Призрачный гонщик
источник
Пожалуйста, отформатируйте код правильно.
Акузьминых
1
Задумывались ли вы о создании фасада, а не о копировании?
ControlAltDel
Не может быть более эффективного способа. Все эти операции должны состояться. Но этот код на самом деле не работает. Вы не помещаете список в пользовательский объект.
user207421

Ответы:

2

Одним из решений является потоковая передача 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())));
        }));
Джейкоб Г.
источник
Вы можете опустить {}оператор return и return в .collect(Collectors.toMap(Function.identity(), entry -> entry.getValue() .entrySet() .stream() .collect(Collectors.toMap(Function.identity(), entry -> getCustomeObj(entry.getValue()))); ));
лямбде
3
@SHoko Правда, но я думаю, что это выглядело бы менее читабельно без блока.
Джейкоб Г.
1

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

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)));
      });
});
Евгений
источник
1

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


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

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

Как насчет 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())))));
Naman
источник