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

时间:2019-10-02 14:44:02

标签: java lambda java-8 java-stream collectors

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

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选项,但无法获得所需的结果。

4 个答案:

答案 0 :(得分:8)

您可以将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())));

答案 1 :(得分:3)

这很简单

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

答案 2 :(得分:2)

要收集Map,其中键是不变的A对象,而值是相应的B对象的列表,可以将toList()收集器替换为以下收集者:

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

first 参数定义了如何从原始对象计算 key identity()使流的原始对象保持不变。

second 参数定义了的计算方式,因此此处仅包含对您的方法的调用,该方法将A转换为以下内容的列表: B

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

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

答案 3 :(得分:2)

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

实际上,如果listofA中存在重复项,则以下代码将引发IllegalStateException

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

可能会引发异常,因为当键冲突时(例如,当键映射器函数返回重复项时,Collectors.toMap不知道如何合并)如果Function.identity()列表中有重复的元素,则listofA就是这种情况。

这是明确说明的in the docs

  

如果映射的键包含重复项(根据Object.equals(Object)),则在执行收集操作时将引发IllegalStateException。如果映射的键可能有重复,请改用toMap(Function, Function, BinaryOperator)。

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

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用于将每个重复的B元素的A个元素添加到每个A的非队列列表中。

在值映射器函数中,创建了一个新的ArrayList,因此每个List<B>的原始A不会发生突变。另外,在创建Arraylist时,我们事先知道可以对其进行突变(即,以后可以在其中添加元素,以防listofA中有重复项)。