我想从Map
List
创建一个Point
,并在地图中包含使用相同parentId映射的列表中的所有条目,例如Map<Long, List<Point>>
。
我使用Collectors.toMap()
但它没有编译:
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.toMap(Point::getParentId, c -> c));
答案 0 :(得分:46)
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
为长,c
为Point
。
而在Map<Long,Point>
应用时collect()
返回。
这种行为很可能是Collectors.toMap() javadoc状态:
返回
Collector
,将元素累积到其键和值的Map
中 是将提供的映射函数应用于输入的结果 元件。
但是如果映射的键包含重复项(根据Object.equals(Object)
),则会抛出IllegalStateException
这可能是您的情况,因为您将根据特定属性Point
对parentId
进行分组。parentId
。
如果映射的键可能有重复项,则可以使用toMap(Function, Function, BinaryOperator)
重载,但它不会真正解决您的问题,因为它不会将具有相同parentId
的元素分组。它只是提供了一种不具有相同public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier)
的两个元素的方法。
<强> Collectors.groupingBy()强>
为了达到您的要求,您应该使用行为和方法声明更适合您需要的Collectors.groupingBy()
:
Function
指定为:
通过&#34;返回实现&#34;组的收集器。输入操作 T类元素,根据分类对元素进行分组 函数,并在Map中返回结果。
该方法需要Function
在您的情况下,Point
参数为type
(Stream的Point.getParentId()
,如果您想按parentId
值对元素进行分组,则返回Map<Long, List<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy( p -> p.getParentId()));
。 / p>
所以你可以写:
Map<Long, List<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId));
或使用方法参考:
groupingBy()
Collectors.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()
中的值,最终{{1}}不是。
答案 1 :(得分:3)
Collectors.groupingBy
正是您想要的,它会根据您的输入集合创建一个地图,使用您为其提供密钥的Function
创建一个条目,并使用您关联的密钥创建一个分数列表,因为它是值。
Map<Long, List<Point>> pointByParentId = chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId));
答案 2 :(得分:3)
以下代码完成了这些工作。 Collectors.toList()
是默认值,因此您可以跳过它,但如果您想要Map<Long, Set<Point>>
Collectors.toSet()
则需要。
Map<Long, List<Point>> map = pointList.stream()
.collect(Collectors.groupingBy(Point::getParentId, Collectors.toList()));
答案 3 :(得分:0)
从object.field到共享此字段的对象集合的映射通常更好地存储在Multimap中(Guava对于multimap有很好的实现)。 如果您不需要多图可变,则可以使用
Multimaps.index(chargePoints, Point::getParentId);
如果必须使用可变映射,则可以实现一个收集器(如此处所示:https://blog.jayway.com/2014/09/29/java-8-collector-for-gauvas-linkedhashmultimap/),也可以使用for循环(或forEach)来填充一个空的可变多图。
多图为您提供了从字段到共享一个字段的对象集合(例如总对象数)的映射时通常需要的其他功能。
可变的多图还使添加和删除图元素变得更加容易(无需考虑边缘情况)。