使用 Collectors.toMap 或 groupingBy 在 Map 中收集 map 操作的结果

Collect results of a map operation in a Map using Collectors.toMap or groupingBy

我有一个 List<A> 类型的列表,并且通过 map 操作获得了一个 List<B> 类型的集合列表,用于合并到一个列表中的所有 A 元素。

List<A> listofA = [A1, A2, A3, A4, A5, ...]

List<B> listofB = listofA.stream()
  .map(a -> repo.getListofB(a))
  .flatMap(Collection::stream)
  .collect(Collectors.toList());

没有平面图

List<List<B>> listOflistofB = listofA.stream()
  .map(a -> repo.getListofB(a))
  .collect(Collectors.toList());

我想将结果收集为 Map<A, List<B>> 类型的地图,到目前为止尝试了各种 Collectors.toMapCollectors.groupingBy 选项,但无法获得所需的结果。

您可以使用带有有限方法引用的 toMap 收集器来获取您需要的内容。另请注意,此解决方案假定您的源容器中没有重复 A 实例。如果该先决条件成立,此解决方案将为您提供所需的结果。这是它的样子。

Map<A, Collection<B>> resultMap = listofA.stream()
    .collect(Collectors.toMap(Function.identity(), repo::getListofB);

如果您有重复的 A 元素,那么除了上面给出的功能外,您还必须使用此合并功能。合并功能处理键冲突(如果有)。

Map<A, Collection<B>> resultMap = listofA.stream()
       .collect(Collectors.toMap(Function.identity(), repo::getListofB, 
            (a, b) -> {
                a.addAll(b);
                return a;
        }));

这里有一个更简洁的 Java9 方法,它使用 flatMapping 收集器来处理重复的 A 元素。

Map<A, List<B>> aToBmap = listofA.stream()
        .collect(Collectors.groupingBy(Function.identity(),
                Collectors.flatMapping(a -> getListofB(a).stream(), 
                        Collectors.toList())));

收集一个Map,key是Aobject不变,value是对应Bobject的列表,您可以用以下收集器替换 toList() 收集器:

toMap(Function.identity(), a -> repo.getListOfB(a))

first 参数定义了如何从原始 object 计算 keyidentity() 采用流的原始object不变。

second 参数定义了 value 的计算方式,因此这里它仅包含对转换 value 的方法的调用=13=] 到 B.

的列表

由于 repo 方法只接受一个参数,您还可以通过将 lambda 替换为方法引用来提高清晰度:

toMap(Function.identity(), repo::getListOfB)

这将是直截了当的,

listofA.stream().collect(toMap(Function.identity(), a -> getListofB(a)));

在这个答案中,我展示了如果您在 List<A> listofA 列表中重复 A 个元素会发生什么。

实际上,如果 listofA 中有重复项,以下代码将抛出 IllegalStateException:

Map<A, Collection<B>> resultMap = listofA.stream()
        .collect(Collectors.toMap(
                            Function.identity(), 
                            repo::getListofB);

可能会抛出异常,因为 Collectors.toMap 不知道如何在键发生冲突时 合并 值(即当键映射器函数 returns 重复,如果 listofA 列表中有重复的元素,Function.identity() 就是这种情况。

这个说的很清楚in the docs:

If the mapped keys contains duplicates (according to Object.equals(Object)), an IllegalStateException is thrown when the collection operation is performed. If the mapped keys may have duplicates, use toMap(Function, Function, BinaryOperator) instead.

文档也给了我们解决方案:如果有重复的元素,我们需要提供一种合并值的方法。这是一种这样的方式:

Map<A, Collection<B>> resultMap = listofA.stream()
        .collect(Collectors.toMap(
                            Function.identity(), 
                            a -> new ArrayList<>(repo.getListofB(a)),
                            (left, right) -> {
                                left.addAll(right);
                                return left;
                            });

这使用了 Collectors.toMap 的重载版本,它接受一个 合并函数 作为它的第三个参数。在合并函数中,Collection.addAll 用于将每个重复的 A 元素的 B 元素添加到每个 A.

的唯一列表中

在值映射器函数中,创建了一个新的ArrayList,这样每个A的原始List<B>就不会发生变异。此外,当我们创建一个 Arraylist 时,我们事先知道它可以被改变(即我们可以稍后向它添加元素,以防 listofA 中有重复项)。