首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >收集到地图中的Collectors.toMap()和Collectors.groupingBy()之间的差异

收集到地图中的Collectors.toMap()和Collectors.groupingBy()之间的差异
EN

Stack Overflow用户
提问于 2017-07-21 07:15:00
回答 4查看 38.6K关注 0票数 41

我希望从List of Points创建一个Points,并将列表中的所有条目都用相同的parentId (如Map<Long, List<Point>> )映射。

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

代码语言:javascript
复制
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
    .collect(Collectors.toMap(Point::getParentId, c -> c));
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-07-21 07:35:31

TLDR :

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

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

Collectors.toMap()

以书面形式:

代码语言:javascript
复制
chargePoints.stream().collect(Collectors.toMap(Point::getParentId, c -> c));

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

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

代码语言:javascript
复制
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper)

它返回一个结果为CollectorMap<K,U>,其中KU是传递给方法的两个函数的返回类型。在您的例子中,Point::getParentId是长的,c指的是Point。而Map<Long,Point>在应用collect()时返回。

这种行为是Collectors.toMap() javadoc声明的结果:

返回一个Collector,它将元素累加到一个Map中,该Map的键和值是将提供的映射函数应用于输入元素的结果。

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

这可能是您的情况,因为您将根据一个特定的属性:PointparentIdparentId进行分组。

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

Collectors.groupingBy()

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

代码语言:javascript
复制
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) 

该条规定如下:

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

该方法采用Function

在您的示例中,Function参数是Point (流的type ),您返回Point.getParentId(),因为您希望按parentId值对元素进行分组。

这样你就可以写:

代码语言:javascript
复制
Map<Long, List<Point>> pointByParentId = 
                       chargePoints.stream()
                                   .collect(Collectors.groupingBy( p -> p.getParentId())); 

或使用方法参考:

代码语言:javascript
复制
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)应该会让您感兴趣。

例如:

代码语言:javascript
复制
Map<Long, Set<Point>> pointByParentId = 
                       chargePoints.stream()
                                   .collect(Collectors.groupingBy(Point::getParentId, toSet()));

因此,除了提出的问题之外,您应该考虑groupingBy()是一种灵活的方法,可以选择您想要存储到所收集的Map中的值,确切地说,toMap()不是。

票数 95
EN

Stack Overflow用户

发布于 2017-07-21 07:23:27

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

代码语言:javascript
复制
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
    .collect(Collectors.groupingBy(Point::getParentId));
票数 4
EN

Stack Overflow用户

发布于 2017-07-21 07:25:55

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

代码语言:javascript
复制
Map<Long, List<Point>> map = pointList.stream()
                .collect(Collectors.groupingBy(Point::getParentId, Collectors.toList()));
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45231351

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档