Collectors.toMap() 和 Collectors.groupingBy() 收集到 Map 的区别

新手上路,请多包涵

I want to create a Map from a List of Points and have inside the map all entries from the list mapped with the same parentId such as Map<Long, List<Point>>

我使用了 Collectors.toMap() 但它没有编译:

 Map<Long, List<Point>> pointByParentId = chargePoints.stream()
    .collect(Collectors.toMap(Point::getParentId, c -> c));

原文由 Tim Schwalbe 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.6k
2 个回答

TLDR :

要收集到包含单个值的 Map 键( Map<MyKey,MyObject> ),请使用 Collectors.toMap()

要收集到包含多个键值的 Map ( Map<MyKey, List<MyObject>> ),请使用 Collectors.groupingBy()


收集器.toMap()

通过写作:

 chargePoints.stream().collect(Collectors.toMap(Point::getParentId, c -> c));

返回的对象将具有 Map<Long,Point> 类型。

查看您正在使用的 Collectors.toMap() 函数:

 Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper)

It returns a Collector with as result Map<K,U> where K and U are the type of return of the two functions passed to the method.在您的情况下, Point::getParentId 是 Long 而 c 指的是 Point 。而 Map<Long,Point> 在应用 collect() 时返回。

正如 Collectors.toMap() javadoc 所述,这种行为是意料之中的:

返回 Collector 将元素累积到 Map 其键和值是将提供的映射函数应用于输入元素的结果。

但是,如果映射的键包含重复项(根据 Object.equals(Object) ),则会抛出 IllegalStateException

这可能是您的情况,因为您将根据特定属性对 Point 进行分组: parentId

如果映射的键可能重复,您可以使用 toMap(Function, Function, BinaryOperator) 重载,但它不会真正解决您的问题,因为它不会将具有相同 parentId 的元素分组。它只会提供一种方法,使两个元素不具有相同的 parentId


收集器.groupingBy()

为了达到您的要求,您应该使用 Collectors.groupingBy() 其行为和方法声明更适合您的需要:

 public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier)

它被指定为:

返回一个 Collector,它对 T 类型的输入元素执行“分组依据”操作,根据分类函数对元素进行分组,并在 Map 中返回结果。

该方法采用 Function

In your case, the Function parameter is Point (the type of Stream) and you return Point.getParentId() as you want to group elements by parentId 值。

所以你可以写:

 Map<Long, List<Point>> pointByParentId =
                       chargePoints.stream()
                                   .collect(Collectors.groupingBy( p -> p.getParentId()));

或者使用方法参考:

 Map<Long, List<Point>> pointByParentId =
                       chargePoints.stream()
                                   .collect(Collectors.groupingBy(Point::getParentId));


Collectors.groupingBy() :更进一步

实际上 groupingBy() 收集器比实际示例走得更远。 Collectors.groupingBy(Function<? super T, ? extends K> classifier) 方法最终只是一种方便的方法,用于将收集到的值 Map 存储在 List 中。

要将 --- 的值存储在不同于 List Map 的其他事物中,或者存储特定计算的结果, groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) ,你应该感兴趣。

例如 :

 Map<Long, Set<Point>> pointByParentId =
                       chargePoints.stream()
                                   .collect(Collectors.groupingBy(Point::getParentId, toSet()));

因此,除了提出的问题之外,您应该考虑 groupingBy() 作为选择要存储到收集的值的灵活方式 Map ,最终 toMap() 不是.

原文由 davidxxx 发布,翻译遵循 CC BY-SA 4.0 许可协议

以下代码完成了这些工作。 Collectors.toList() 是默认的,所以你可以跳过它,但如果你想要 Map<Long, Set<Point>> Collectors.toSet()

 Map<Long, List<Point>> map = pointList.stream()
                .collect(Collectors.groupingBy(Point::getParentId, Collectors.toList()));

原文由 xenteros 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题