Объединить список карт в одну карту путем суммирования значений - PullRequest
0 голосов
/ 10 февраля 2019

У меня есть класс Значения:

public class Values {
    private int count;
    private int values;
}

И список нескольких карт типа Map<String, Values>

Map<String, Values> map1 = new HashMap<>();
        map1 .put("aaa", new Values(1, 10));
        map1 .put("bbb", new Values(5, 50));
        map1 .put("ccc", new Values(2, 30));

Map<String, Values> map2= new HashMap<>();
        map2.put("aaa", new Values(2, 20));
        map2.put("bbb", new Values(3, 50));
        map2.put("ccc", new Values(3, 10));

List<Map<String, Values>> list = Arrays.asList(map1, map2);

Может быть любое количество карт и любое количество записейвнутри карт, но ключи для карт всегда одинаковы, только значения могут отличаться.Мой пример содержит только 2 карты и только 3 записи для каждой карты для ясности.

Я хочу получить одну карту с теми же ключами и объектами Values, как сумма каждого «количества» и каждого «значения» в объектах, например:

{
        aaa= {
            count = 3,
            value = 30
        },
        bbb= {
            count = 8,
            value = 100
        },
        ccc= {
            count = 6,
            value = 40
        }
    }

Я пытаюсь добиться этого с помощью API Streams, но я застрял:

public static Map<String, Values> mergeMaps(List<Map<String, Values>> maps) {
        return maps.stream()
                .flatMap(m -> m.entrySet().stream()) ...?
    }

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

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 10 февраля 2019

Еще одно решение с groupingBy:

Map<String, Optional<Values>> collect = list.stream()
            .flatMap(map -> map.entrySet().stream())
            .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue,
                    reducing((v1, v2) -> new Values(v1.count + v2.count, v1.values + v2.values)))));

Примечание: значения этой карты Optional<Values>.Если у вас есть значение null на одной из ваших исходных карт, например map2.put("ddd", null);, это позволяет избежать NullPointerException и вернуть Optional.empty

0 голосов
/ 10 февраля 2019

Вы можете собирать записи вашего Stream с помощью коллектора toMap и функции слияния.

public static Map<String, Values> mergeMaps(List<Map<String, Values>> maps) {
    return maps.stream()
               .flatMap(m -> m.entrySet().stream())
               .collect(Collectors.toMap(Map.Entry::getKey,
                                         Map.Entry::getValue,
                                         (v1,v2) -> new Values(v1,v2)));
}

Предполагая, что у вас есть конструктор Values, который принимает два экземпляра Values исоздает экземпляр, имеющий суммы значений.

Конечно, вы можете написать функцию слияния без этого конструктора.Например:

(v1,v2) -> new Values(v1.getCount()+v2.getCount(),v1.getValue()+v2.getValue())
...