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

Tim*_*lbe 22 java java-8 collectors

我想创建一个MapListPointS和有地图从里面用相同的parentId,如映射列表中的所有条目Map<Long, List<Point>>.
我用过Collectors.toMap()但不编译:

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

dav*_*xxx 59

TLDR:

Map通过key(Map<MyKey,MyObject>)收集包含单个值的内容,请使用Collectors.toMap().
Map通过key(Map<MyKey, List<MyObject>>)收集包含多个值的内容,请使用Collectors.groupingBy().


Collectors.toMap()

通过写:

chargePoints.stream().collect(Collectors.toMap(Point::getParentId, c -> c));
Run Code Online (Sandbox Code Playgroud)

返回的对象将具有该Map<Long,Point>类型.
查看Collectors.toMap()您正在使用的功能:

Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper)
Run Code Online (Sandbox Code Playgroud)

它返回a Collector作为结果Map<K,U>where KU是传递给方法的两个函数的返回类型.在你的情况下,Point::getParentId是一个龙,并c指一个Point.而 应用Map<Long,Point>时返回的collect()是.

Collectors.toMap() javadoc指出这种行为很可取:

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

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

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


Collectors.groupingBy()

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

public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) 
Run Code Online (Sandbox Code Playgroud)

它被指定为:

返回一个收集器,对T类型的输入元素执行"group by"操作,根据分类函数对元素进行分组,并将结果返回到Map中.

该方法需要一个Function.
在您的情况下,Function参数是Point(typeStream的),并且Point.getParentId()您希望按parentId值对元素进行分组.

所以你可以写:

Map<Long, List<Point>> pointByParentId = 
                       chargePoints.stream()
                                   .collect(Collectors.groupingBy( p -> p.getParentId())); 
Run Code Online (Sandbox Code Playgroud)

或者使用方法参考:

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

Collectors.groupingBy():走得更远

事实上,groupingBy()收藏家比实际例子更进一步.该Collectors.groupingBy(Function<? super T, ? extends K> classifier)方法是最后只存储所收集的值的简便方法MapList.
要存储Map除了List或存储特定计算结果之外的其他内容的值,groupingBy(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()));
Run Code Online (Sandbox Code Playgroud)

因此,除了提出的问题之外,您应该考虑groupingBy()选择要存储到收集的值中的灵活方式Map,明确toMap()不是.

  • @TimSchwalbe on [so]而不是说谢谢我们upvote并接受答案. (2认同)