Java Collectors.toMap() 和 Collectors.groupingBy() 收集到 Map 的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45231351/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Differences between Collectors.toMap() and Collectors.groupingBy() to collect into a Map
提问by Tim Schwalbe
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
从 a创建一个List
,Points
并在映射中将列表中的所有条目映射到相同的 parentId,例如Map<Long, List<Point>>
.
我用过Collectors.toMap()
但它不编译:
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.toMap(Point::getParentId, c -> c));
采纳答案by davidxxx
TLDR :
TLDR :
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()
.
要Map
通过键 ( Map<MyKey,MyObject>
)收集到包含单个值的 a ,请使用Collectors.toMap()
.
要Map
按键 ( Map<MyKey, List<MyObject>>
)收集到包含多个值的a ,请使用Collectors.groupingBy()
.
Collectors.toMap()
Collectors.toMap()
By writing :
通过写作:
chargePoints.stream().collect(Collectors.toMap(Point::getParentId, c -> c));
The returned object will have the Map<Long,Point>
type.
Look at the Collectors.toMap()
function that you are using :
返回的对象将具有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.
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.
它返回一个Collector
with 作为结果Map<K,U>
,其中K
和U
是传递给该方法的两个函数的返回类型。在您的情况下,Point::getParentId
是 Long 并c
指代Point
. 而 应用Map<Long,Point>
返回的collect()
时间。
And this behavior is rather expected as Collectors.toMap()javadoc states :
正如Collectors.toMap()javadoc 所说,这种行为是相当预期的:
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.
返回 a
Collector
将元素累积到 a 中,Map
其键和值是将提供的映射函数应用于输入元素的结果。
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
.
但是,如果映射的键包含重复项(根据Object.equals(Object)
),IllegalStateException
则会抛出an
这可能是您的情况,因为您将Point
根据特定属性对s 进行分组: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
.
如果映射的键可能有重复项,您可以使用toMap(Function, Function, BinaryOperator)
重载,但它不会真正解决您的问题,因为它不会将具有相同parentId
. 它只会提供一种方法,使两个元素不具有相同的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 :
为了实现您的要求,您应该使用Collectors.groupingBy()
更适合您需要的行为和方法声明:
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier)
It is specified as :
它被指定为:
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.
返回一个对类型 T 的输入元素执行“分组依据”操作的收集器,根据分类函数对元素进行分组,并在 Map 中返回结果。
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.
该方法采用Function
.
在您的情况下,Function
参数是Point
(the type
of Stream) 并且您Point.getParentId()
根据parentId
值对元素进行分组而返回。
So you could write :
所以你可以写:
Map<Long, List<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy( p -> p.getParentId()));
Or with a method reference :
或使用方法参考:
Map<Long, List<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId));
Collectors.groupingBy() : go further
Collectors.groupingBy() :走得更远
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.
实际上,groupingBy()
收集器比实际示例更进一步。该Collectors.groupingBy(Function<? super T, ? extends K> classifier)
方法是最后只存储所收集的值的简便方法Map
的List
。
将 the 的值存储Map
在 a 以外的其他事物中List
或存储特定计算的结果,groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
您应该感兴趣。
For example :
例如 :
Map<Long, Set<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId, toSet()));
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.
因此超越问的问题,你应该考虑groupingBy()
作为一个灵活的方式来选择值要存储到收集Map
,明确什么toMap()
是没有的。
回答by Patrick
Collectors.groupingBy
is exactly what you want, it creates a Map from your input collection, creating an Entry using the Function
you provide for it's key, and a List of Points with your associated key as it's value.
Collectors.groupingBy
正是您想要的,它从您的输入集合创建一个 Map,使用Function
您提供的键创建一个条目,以及一个带有关联键作为值的点列表。
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId));
回答by xenteros
The following code does the stuff. Collectors.toList()
is the default one, so you can skip it, but in case you want to have Map<Long, Set<Point>>
Collectors.toSet()
would be needed.
下面的代码做的东西。Collectors.toList()
是默认的,所以你可以跳过它,但如果你想拥有它Map<Long, Set<Point>>
Collectors.toSet()
是需要的。
Map<Long, List<Point>> map = pointList.stream()
.collect(Collectors.groupingBy(Point::getParentId, Collectors.toList()));
回答by Roy Shahaf
It is quite often true that a map from object.field to Collection of objects that share this field is better stored in a Multimap (Guava has a nice implementation for a multimap). If you don't NEED the multimap to be mutable (which should be the desired case), you can use
通常情况下,从 object.field 到共享该字段的对象集合的映射最好存储在 Multimap 中(Guava 有一个很好的 multimap 实现)。如果您不需要 multimap 是可变的(这应该是所需的情况),您可以使用
Multimaps.index(chargePoints, Point::getParentId);
If you must use a mutable map, you can either implement a collector (as demonstrated here: https://blog.jayway.com/2014/09/29/java-8-collector-for-gauvas-linkedhashmultimap/) or use a for loop (or forEach) to populate an empty, mutable multimap.
如果您必须使用可变映射,您可以实现一个收集器(如下所示:https: //blog.jayway.com/2014/09/29/java-8-collector-for-gauvas-linkedhashmultimap/)或使用一个 for 循环(或 forEach)来填充一个空的、可变的多映射。
A multimap gives you additional functionality that you normally need when you use a map from field to collection of objects sharing a field (like the count of total objects).
当您使用从字段到共享字段的对象集合的映射(例如对象总数)时,多映射为您提供了通常需要的附加功能。
A mutable multimap also makes it easier to add and remove elements to the map (without being concerned with the edge cases).
可变多映射还可以更轻松地向映射添加和删除元素(无需考虑边缘情况)。