如何使用 Java lambda 将 List<Foo> 收集到 Map<Bar, List<Baz>>?

How to collect a List<Foo> to a Map<Bar, List<Baz>> using Java lambdas?

我有一个对象 Foo,它引用了 BarBaz 对象:

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 变成 List<Baz>。是否有我没有看到的值的 lambda 转换?

我想你想要

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

其中 getBaz 是 "downstream collector",它转换分组的 Foos,然后另一个创建列表。

要将经典 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())));

你很接近,你需要提供一个 "downstream" 收集器来进一步完善你的标准。在这种情况下,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::getBarkeyMapper 提取映射键的函数。
  • v -> new ArrayList<>(singletonList(v))valueMapper 提取地图值的函数。
  • (l, r) -> {l.addAll(r); return l;} 是用来合并的函数 合并恰好具有相同 getBar 值的两个列表。