收集到地图中的Collectors.toMap()和Collectors.groupingBy()之间的区别 [英] Differences between Collectors.toMap() and Collectors.groupingBy() to collect into a Map

查看:729
本文介绍了收集到地图中的Collectors.toMap()和Collectors.groupingBy()之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从PointsList创建一个Map,并在地图内部具有使用相同的parentId(例如Map<Long, List<Point>>)映射的列表中的所有条目. 我使用了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<MyKey,MyObject>)收集到包含单个值的Map中,请使用

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<Long,Point>类型.
查看您正在使用的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>,其中KU是传递给该方法的两个函数的返回类型. 在您的情况下,Point::getParentId是Long,而c是指Point. 而Map<Long,Point>在应用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.

而且,此行为应作为

And this behavior is rather expected as Collectors.toMap() javadoc states :

返回一个Collector,该元素将元素累积到Map中,其键和值 是将提供的映射功能应用于输入的结果 元素.

returns a Collector that accumulates elements into a Map whose keys and values are the result of applying the provided mapping functions to the input elements.

但是,如果映射的键包含重复项(根据Object.equals(Object)),则会抛出IllegalStateException.
您可能会遇到这种情况,因为您将根据特定属性parentIdPoint进行分组.

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 Points according to a specific property : 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()

要满足您的要求,您应该使用

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),并且您想按parentId值对元素进行分组时会返回Point.getParentId().

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的值存储在List中的一种便捷方法.
要将Map的值存储在除List之外的其他内容中或存储特定计算的结果,groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)应该引起您的兴趣.

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()之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆