Point
的List
中创建一个Map
,并在地图内将列表中的所有条目与相同的父ID映射,例如:Map<Long,List<Point>>
。我使用了
Collectors.toMap()
,但它无法编译:Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.toMap(Point::getParentId, c -> c));
Point
的List
中创建一个Map
,并在地图内将列表中的所有条目与相同的父ID映射,例如:Map<Long,List<Point>>
。Collectors.toMap()
,但它无法编译:Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.toMap(Point::getParentId, c -> c));
TLDR :
若要将单个值按键(Map<MyKey,MyObject>
)收集到Map
中,请使用Collectors.toMap()
。
若要将多个值按键(Map<MyKey, List<MyObject>>
)收集到Map
中,请使用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
类型。
当应用于collect()
时,返回Map<Long,Point>
。如果映射键包含重复项(根据返回一个
Collector
,将元素累积到Map
中,其键和值是将所提供的映射函数应用于输入元素的结果。
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)
这个方法的作用是:
返回一个Collector,该Collector实现了对类型为T的输入元素执行“group by”操作,根据分类函数对元素进行分组,并将结果返回到Map中。
该方法接受一个Function
参数。
在您的情况下,Function
参数是Point
(Stream的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
作为它的键来创建一个Entry,并将带有你关联键的点的列表作为它的值。
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId));
Map<Long, List<Point>> map = pointList.stream()
.collect(Collectors.groupingBy(Point::getParentId, Collectors.toList()));
通常情况下,从对象.字段到共享该字段的对象集合的映射最好存储在Multimap中(Guava有一个不错的Multimap实现)。 如果您不需要让Multimap可变(这应该是期望的情况),您可以使用
Multimaps.index(chargePoints, Point::getParentId);
如果您必须使用可变映射,您可以实现一个收集器(如此处所示:https://blog.jayway.com/2014/09/29/java-8-collector-for-gauvas-linkedhashmultimap/),或使用for循环(或forEach)来填充一个空的、可变的多重映射。
多重映射为您提供了额外的功能,通常在您使用从字段到共享字段对象集合的映射时需要这些功能(例如总对象计数)。
可变多重映射还使添加和删除元素更容易(无需考虑边缘情况)。
Collectors.groupingBy
。 - Louis Wasserman