Группировка потоков Java8 с помощью проблемы toMap - PullRequest
2 голосов
/ 09 июля 2020

У меня есть класс с именем ConfigKey

public class ConfigKey {
    String code;
    String key;
    String value;
    //omit setter and getter
}

Я хочу преобразовать List<ConfigKey> в Map<String, Map<String, Object>>, вот определение моего метода

public Map<String, Map<String, Object> convert (List<ConfigKey> list) {
    return list.stream().collect(Collectors.groupingBy(ConfigKey::getCode, 
            Collectors.toMap(ConfigKey::getKey, ConfigKey::getValue)));
}

, но я хочу внесите некоторые изменения, для каждого ConfigKey поместите другой ключ на карту, например,

{ "code": "code1","key", "key1", "value": "value1"}
to Map
{"code1": {"key1":"value1", "prefix_key1": "value1" }

есть ли какой-либо API, чтобы сделать это, как показано ниже:

public Map<String, Map<String, Object> convert (List<ConfigKey> list) {
    return list.stream().collect(Collectors.groupingBy(ConfigKey::getCode, 
            Collectors.toMap("prefix_" + ConfigKey::getKey, ConfigKey::getValue))
            Collectors.toMap(ConfigKey::getKey, ConfigKey::getValue)));
}

Ответы [ 2 ]

3 голосов
/ 09 июля 2020

Вы можете использовать фабричный метод Collector.of(), который позволяет вам создать свой собственный сборщик:

public Map<String, Map<String, Object> convert (List<ConfigKey> list) {
    return list.stream().collect(Collectors.groupingBy(ConfigKey::getCode, Collector.of(
        HashMap::new, (m, c) -> {
            m.put(c.getKey(), c.getValue());
            m.put("prefix_" + c.getKey(), c.getValue());
        }, (a, b) -> {
            a.putAll(b);
            return b;
        }
    )));
}

Но, честно говоря, это кажется немного беспорядочным, и, возможно, нормальный l oop будет было лучше. Намерение streams состояло в том, чтобы предоставить api, который делает вещи более читаемыми, но когда вам нужно обойти эту конструкцию, введя некоторые чрезвычайно нечитаемые logi c, тогда почти всегда лучший вариант просто сделать это старый способ:

public Map<String, Map<String, Object> convert (List<ConfigKey> list) {
    Map<String, Map<String, Object>> map = new HashMap<>();
    for (ConfigKey ck : list) {
        Map<String, Object> inner = map.computeIfAbsent(ck.getCode(), k -> new HashMap<>());
        inner.put(ck.getKey(), ck.getValue());
        inner.put("prefix_" + ck.getKey(), ck.getValue());
    }
    return map;
}

0 голосов
/ 09 июля 2020

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

private Map<String, Map<String, Object>> convert(List<ConfigKey> list) {
    new ArrayList<>(list).stream().map(configKey -> new ConfigKey(configKey.getCode(), "prefix_" + configKey.getKey(), configKey.getValue())).forEachOrdered(list::add);
    return list.stream().collect(Collectors.groupingBy(ConfigKey::getCode,
            Collectors.toMap(ConfigKey::getKey, ConfigKey::getValue)));
}

Я клонировал список (чтобы предотвратить ConcurrentModificationException), затем изменил ключи на «новые» ( с картой) и добавил их в исходный список - forEachOrdered (list :: add). Поскольку поле 'code' не было изменено, его будут использовать обе записи, в результате чего на карте появятся 2 записи

...