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

2022-09-01 02:51:00

我想创建一个 from a of,并在映射中将列表中的所有条目映射到相同的父 Id,例如 .
我用过,但它没有编译:MapListPointsMap<Long, List<Point>>Collectors.toMap()

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

答案 1

TLDR :

若要按键 () 收集到包含单个值的 中,请使用 Collectors.toMap()。
若要按键 () 收集到包含多个值的 中,请使用 Collectors.groupingBy()MapMap<MyKey,MyObject>MapMap<MyKey, List<MyObject>>


Collectors.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)

它返回 with 作为结果,其中 和 是传递给该方法的两个函数的返回类型。在您的情况下,是一个 Long,指的是 .而返回的时间是应用 on。CollectorMap<K,U>KUPoint::getParentIdcPointMap<Long,Point>collect()

这种行为是意料之中的,因为 Collectors.toMap() javadoc 声明:

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

但是,如果映射的键包含重复项(根据 ),则可能会抛出
a,这可能是您的情况,因为您将根据特定属性对 s 进行分组:。Object.equals(Object)IllegalStateExceptionPointparentId

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


Collectors.groupingBy()

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

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

它被指定为:

返回一个收集器,该收集器对 T 类型的输入元素实现“分组依据”操作,根据分类函数对元素进行分组,并在 Map 中返回结果。

该方法采用 .
在您的例子中,参数是(Stream 的 ),并且您可以返回,因为您希望按值对元素进行分组。FunctionFunctionPointtypePoint.getParentId()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() : 走得更远

事实上,收集器比实际示例走得更远。该方法最终只是一种将收集的值存储在 .
要将 的值存储在另一个事物中,而不是 a 或 存储特定计算的结果,您应该会感兴趣。groupingBy()Collectors.groupingBy(Function<? super T, ? extends K> classifier)MapListMapListgroupingBy(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()MaptoMap()


答案 2

Collectors.groupingBy正是您想要的,它会从您的输入集合中创建一个 Map,使用您为其键提供的条目创建一个条目,以及一个具有关联键的点列表作为其值。Function

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

推荐