Ефективний спосіб ітерації та копіювання значень 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

Не може бути більш ефективного способу. Усі ці операції мають відбутися. Але цей код насправді не працює. Ви не вводите список у спеціальний об'єкт.
користувач207421

Відповіді:


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

Ви можете опустити {}та повернути заяву в лямбда, приблизно так:.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

Потокове передавання IMHO - не така вже й погана ідея. Немає поганих інструментів. Це залежить від того, як ви їх використовуєте.


У цьому конкретному випадку я витягну повторюваний зразок у корисний метод:

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())))));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.