Потоки Java: получать значения, сгруппированные по внутренней карте

У меня есть Map<A, Map<B, C>> и я хочу получить от нее Map<B, List<C>> с помощью потоков Java.

Я пытаюсь сделать это следующим образом:

public <A, B, C> Map<B, List<C>> groupsByInnerKey(Map<A, Map<B, C>> input) {
    return input.values()
            .stream()
            .flatMap(it -> it.entrySet().stream())
            .collect(Collectors.groupingBy(Map.Entry::getKey));
}

Что я ожидаю:

  • flatMap дает Stream of Map.Entry<B, C>
  • collect(Collectors.groupingBy(...)) принимает функцию, которая применяется к Map.Entry<B, C> и возвращает B, поэтому она собирает значения C в List<C>.

Но он не компилируется, буквально:

Нестатический метод не может ссылаться на статический контекст

на Map.Entry::getKey в последней строке.

Может ли кто-нибудь объяснить, что не так, или что такое правильный способ добиться того, чего я хочу?

Ответ 1

Ваш поток состоит из объектов Map.Entry но вы хотите, чтобы вы собирали на самом деле значение записи, а не самой записи. С помощью вашего текущего кода вы получите Map<B, List<Map.Entry<B, C>>>.

Таким образом, вы просто пропускаете вызов Collectors.mapping. Этот сборщик будет отображать элемент Stream с заданной функцией mapper и собирать этот результат в нижестоящий контейнер. В этом случае картографом является Map.Entry::getValue (поэтому возвращающее значение из записи карты), а сборщик нисходящего потока собирает в List.

public <A, B, C> Map<B, List<C>> groupsByInnerKey(Map<A, Map<B, C>> input) {
    return input.values()
            .stream()
            .flatMap(it -> it.entrySet().stream())
            .collect(Collectors.groupingBy(
                 Map.Entry::getKey,
                 Collectors.mapping(Map.Entry::getValue, Collectors.toList())
            ));
}

Ответ 2

Конвейер потока возвращает Map<B, List<Map.Entry<B,C>>>, а не Map<B, List<C>>.

Чтобы получить Map<B, List<C>>, вам нужно добавить mapping которое будет отображать Map.Entry<B,C> в C:

return input.entrySet()
        .stream()
        .flatMap(it -> it.getValue().entrySet().stream())
        .collect(Collectors.groupingBy(Map.Entry::getKey,Collectors.mapping(Map.Entry::getValue,Collectors.toList())));