![](/img/trans.png)
[英]Collect results of a map operation in a Map using Collectors.toMap or groupingBy
[英]Differences between Collectors.toMap() and Collectors.groupingBy() to collect into a Map
我想從Points
List
創建一個Map
,並在地圖中將列表中的所有條目映射到相同的 parentId,例如Map<Long, List<Point>>
。
我使用了Collectors.toMap()
但它不能編譯:
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.toMap(Point::getParentId, c -> c));
TLDR :
收集到Map
包含由密鑰的單個值( Map<MyKey,MyObject>
使用Collectors.toMap()
為了收集到一個Map
,通過鍵包含多個值( Map<MyKey, List<MyObject>>
),使用Collectors.groupingBy()
Collectors.toMap()
通過寫作:
chargePoints.stream().collect(Collectors.toMap(Point::getParentId, c -> c));
返回的對象將具有Map<Long,Point>
類型。
查看您正在使用的Collectors.toMap()
函數:
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
它返回一個Collector
,結果為Map<K,U>
,其中K
和U
是傳遞給該方法的兩個函數的返回類型。 在您的情況下, Point::getParentId
是 Long , c
指的是Point
。 而Map<Long,Point>
在應用collect()
時返回。
正如Collectors.toMap() javadoc 所說,這種行為是相當預期的:
返回一個將元素累積到
Map
的Collector
,其鍵和值是將提供的映射函數應用於輸入元素的結果。
但是如果映射的鍵包含重復項(根據Object.equals(Object)
),則會IllegalStateException
這可能是您的情況,因為您將根據特定屬性對Point
分組: 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)
它被指定為:
返回一個對類型 T 的輸入元素執行“分組依據”操作的收集器,根據分類函數對元素進行分組,並在 Map 中返回結果。
該方法采用一個Function
。
在您的情況下, Function
參數是Point
(流的type
)並且您返回Point.getParentId()
因為您希望按parentId
值對元素進行分組。
所以你可以寫:
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)
應該感興趣你。
例如:
Map<Long, Set<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId, toSet()));
因此,除了提出的問題之外,您應該考慮groupingBy()
作為一種靈活的方式來選擇要存儲到收集的Map
,而toMap()
絕對不是。
Collectors.groupingBy
正是您想要的,它從您的輸入集合創建一個 Map,使用您為其鍵提供的Function
創建一個條目,以及一個帶有關聯鍵作為值的點列表。
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId));
下面的代碼做的東西。 Collectors.toList()
是默認的,所以你可以跳過它,但如果你想要Map<Long, Set<Point>>
Collectors.toSet()
將需要。
Map<Long, List<Point>> map = pointList.stream()
.collect(Collectors.groupingBy(Point::getParentId, Collectors.toList()));
通常情況下,從 object.field 到共享該字段的對象集合的映射最好存儲在 Multimap 中(Guava 有一個很好的 multimap 實現)。 如果您不需要 multimap 是可變的(這應該是所需的情況),您可以使用
Multimaps.index(chargePoints, Point::getParentId);
如果您必須使用可變映射,您可以實現一個收集器(如下所示: https : //blog.jayway.com/2014/09/29/java-8-collector-for-gauvas-linkedhashmultimap/ )或使用一個 for 循環(或 forEach)來填充一個空的、可變的多映射。
當您使用從字段到共享字段的對象集合的映射(例如對象總數)時,多映射為您提供了通常需要的附加功能。
可變多映射還可以更輕松地向映射添加和刪除元素(無需考慮邊緣情況)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.