Collectors.toMap() 和 Collectors.groupingBy() 收集到 Map 的区别 [英] Differences between Collectors.toMap() and Collectors.groupingBy() to collect into a Map
问题描述
我想从 Points
的 List
创建一个 Map
并在地图内部使用相同的 parentId 映射列表中的所有条目例如Map
.
我使用了 Collectors.toMap()
但它不能编译:
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>>
.
I used Collectors.toMap()
but it doesn't compile :
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.toMap(Point::getParentId, c -> c));
推荐答案
TLDR :
要通过键 (Map
) 收集到包含单个值的 Map
,请使用 Collectors.toMap()
.
要通过键 (Map
) 收集到包含多个值的 Map
,请使用 Collectors.groupingBy()
.
To collect into a Map
that contains a single value by key (Map<MyKey,MyObject>
), use Collectors.toMap()
.
To collect into a Map
that contains multiple values by key (Map<MyKey, List<MyObject>>
), use Collectors.groupingBy()
.
Collectors.toMap()
通过写作:
chargePoints.stream().collect(Collectors.toMap(Point::getParentId, c -> c));
返回的对象将具有 Map
类型.
查看您正在使用的 Collectors.toMap()
函数:
The returned object will have the Map<Long,Point>
type.
Look at the Collectors.toMap()
function that you are using :
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
它返回一个 Collector
作为结果 Map
其中 K
和 U
是传递给方法的两个函数的返回类型.在您的情况下,Point::getParentId
是 Long 并且 c
指的是 Point
.而 Map
在应用 collect()
时返回.
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.
In your case, Point::getParentId
is a Long and c
refers to a Point
.
Whereas the Map<Long,Point>
returned when collect()
is applied on.
而这种行为是相当预期的 Collectors.toMap() javadoc 状态:
And this behavior is rather expected as Collectors.toMap() javadoc states :
返回一个 Collector
将元素累积到一个 Map
中,它的键和值是将提供的映射函数应用于输入的结果元素.
returns a
Collector
that accumulates elements into aMap
whose keys and values are the result of applying the provided mapping functions to the input elements.
但如果映射的键包含重复项(根据Object.equals(Object)
),则会抛出IllegalStateException
这可能是您的情况,因为您将根据特定属性对 Point
进行分组:parentId
.
But if the mapped keys contains duplicates (according to Object.equals(Object)
), an IllegalStateException
is thrown
It will be probably your case as you will group the Point
s according to a specific property : parentId
.
如果映射的键可能有重复项,您可以使用 toMap(Function, Function, BinaryOperator)
重载,但它不会真正解决您的问题,因为它不会对具有相同 parentId
的元素进行分组.它只会提供一种方法,使两个元素不具有相同的 parentId
.
If the mapped keys may have duplicates, you could use the toMap(Function, Function, BinaryOperator)
overload but it will not really solve your problem as it will not group elements with the same parentId
. It will just provide a way to not have two elements with the same parentId
.
Collectors.groupingBy()
要实现您的要求,您应该使用 Collectors.groupingBy()
行为和方法声明更适合您的需要:
To achieve your requirement, you should use Collectors.groupingBy()
which the behavior and the method declaration suits much better to your need :
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier)
指定为:
返回一个对输入执行分组依据"操作的收集器T 类型的元素,根据分类对元素进行分组函数,并在 Map 中返回结果.
Returns a Collector implementing a "group by" operation on input elements of type T, grouping elements according to a classification function, and returning the results in a Map.
该方法需要一个Function
.
在您的情况下,Function
参数是 Point
(Stream 的 type
)并且您返回 Point.getParentId()
因为您想按 parentId
值对元素进行分组.
The method takes a 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
values.
所以你可以写:
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
的值存储在 列表
.
要将 Map
的值存储在 List
以外的其他东西中或存储特定计算的结果,groupingBy(Function super T, ? extends K>; 分类器, 收集器 super T, A, D>下游)
应该会让你感兴趣.
Indeed the groupingBy()
collector goes further than the actual example.
The Collectors.groupingBy(Function<? super T, ? extends K> classifier)
method is finally just a convenient method to store the values of the collected Map
in a List
.
To store values of the Map
in another thing than a List
or to store the result of a specific computation , groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
should interest you.
例如:
Map<Long, Set<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId, toSet()));
因此,除了提出的问题之外,您还应该考虑将 groupingBy()
作为一种灵活的方式来选择要存储到收集的 Map
中的值,具体是什么 toMap()
不是.
So beyond the asked question, you should consider groupingBy()
as a flexible way to choose values that you want to store into the collected Map
, what definitively toMap()
is not.
这篇关于Collectors.toMap() 和 Collectors.groupingBy() 收集到 Map 的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!