Как собрать List <Foo> в Map <Bar, List <Baz>> с помощью лямбда-выражений Java?

У меня есть объект Foo, который имеет ссылки на объекты Bar и Baz:

public class Foo {
    private Bar bar;
    private Baz baz;

    public Foo(Bar bar, Baz baz) {
       this.bar = bar;
       this.baz = baz;
    }
}

У меня есть List<Foo>, который я хотел бы преобразовать в Map. Я бы хотел, чтобы ключом был Bar, а значением - List<Baz>.

Я могу создать Map, где Bar является ключом, а значение - List<Foo>:

Map<Bar, List<Foo>> mapBarToFoos = foos.stream().collect(Collectors.groupingBy(Foo::getBar));

Я не знаю, как сделать последний шаг и превратить список значений в List<Baz>. Есть ли лямбда-преобразование для значения, которое я не вижу?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
91
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Я думаю ты хочешь

list.stream().collect(groupingBy(Foo::getBar, 
    mapping(Foo::getBaz, toList())));

Где getBaz - это «нисходящий сборщик», который преобразует сгруппированные Foo, затем еще один, который создает список.

Правильная идея, вам просто нужно передать нижний коллектор на mapping

Ousmane D. 11.12.2018 23:10

Наконец-то пришлось попробовать - сработало как шарм. Другие были столь же мудры в своих советах, но вы были первым. Спасибо!

duffymo 12.12.2018 18:04

Чтобы заменить классический grouping на Map<Bar, List<Foo>>, вам нужно использовать метод, который позволяет изменять значения карты:

//Use the import to reduce the Collectors. redundancy
//import static java.util.stream.Collectors.*;

Map<Bar, List<Baz>> mapBarToFoos = 
         foos.stream().collect(groupingBy(Foo::getBar, mapping(Foo::getBaz, toList())));

//without you'll get
Map<Bar, List<Baz>> mapBarToFoos = 
         foos.stream().collect(Collectors.groupingBy(Foo::getBar, Collectors.mapping(Foo::getBaz, Collectors.toList())));

Вы близки, вам нужно будет поставить "нижний" коллектор для дальнейшего уточнения ваших критериев. в этом случае подход groupingBy вместе с нижележащим коллектором mapping является идиоматическим подходом, т.е.

list.stream().collect(groupingBy(Foo::getBar, mapping(Foo::getBaz, toList())));

По сути, это работает путем применения нижележащего сборщика mapping к результатам функции классификации (Foo::getBar). в основном то, что мы сделали, - это сопоставили каждый объект Foo с Baz и поместили его в список.


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

foos.stream()
    .collect(toMap(Foo::getBar, v -> new ArrayList<>(singletonList(v.getBaz())),
            (l, r) -> {l.addAll(r); return l;}));
  • Foo::getBar - это функция keyMapper для извлечения ключей карты.
  • v -> new ArrayList<>(singletonList(v)) - это valueMapper функция для извлечения значений карты.
  • (l, r) -> {l.addAll(r); return l;} - это функция слияния, используемая для объедините два списка, которые имеют одинаковое значение getBar.

Другие вопросы по теме