我的目标是将列表中每个项目的计数存储在Map中。这可以通过groupingBy()
和counting()
方法来实现。
我的下一个约束是,对于不在列表中的值,我仍然需要将该键设置为0的映射。因此,必须定义所有可能的值。
这是我想出的:
Map<String, Long> EMPTY = Map.of("a", 0L,
"b", 0L,
"c", 0L,
"d", 0L);
List<String> list = List.of("a", "a", "d", "c", "d", "c", "a", "d");
Map<String, Long> count = list.stream()
.collect(groupingBy(s -> s,
() -> new HashMap<>(EMPTY),
counting()));
此代码引发以下异常:
Exception in thread "main" java.lang.ClassCastException: class java.lang.Long cannot be cast to class [J (java.lang.Long and [J are in module java.base of loader 'bootstrap')
at java.base/java.util.stream.Collectors.lambda$groupingBy$53(Collectors.java:1129)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at Test.main(Test.java:18)
但是,如果我仅将new HashMap<>(EMPTY)
替换为new HashMap<>()
,代码就可以正常工作。
我通过不使用空Map进行收集过程违反了某些规定吗?否则我将如何使用流实现我的目标?
答案 0 :(得分:3)
有点怪异的错误。具体来说,您正在使用的收集器(通过使用Collectors.counting
)实际上是累积到原始long
s的单个元素数组中。
public static <T> Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper)
{
return new CollectorImpl<>(
() -> new long[1],
(a, t) -> { a[0] += mapper.applyAsLong(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
当groupingBy
执行computeIfAbsent
时,期望得到一个long[]
,但是由于您已经具有“ a”的密钥,因此您会返回一个Long
与累加器接受的类型不匹配。那就是引发异常的原因。
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(container, t);
稍后,它们replace所有地图值:
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
使用上面定义的“修整器”(a -> a[0]
)从long[]
到Long
s。
是的,这有点顽皮,但您违反了合同
mapFactory:提供新的空地图的供应商, 结果将被插入
所以也很公平。他们正在使用HashMap
,在编译时将其确定为Map<String, Long>
,并将long[]
放入其中。之所以可能是因为generics are not reified。在运行时,它只是一个HashMap
,能够存储任何类型的键和值。
答案 1 :(得分:2)
Collectors.groupingBy(classifier, mapFactory, downstream)
的文档规定
参数:
- 分类器-将输入元素映射到键的分类器函数
- 下游-实施下游减排的收集器
- mapFactory-提供新的空地图并将结果插入其中的供应商*
答案 2 :(得分:1)
流式传输列表,并使用groupingBy
和counting
将其转换为Map<String,Long>
,然后使用foreach
映射中的EMPTY
更新值
list.stream().collect(Collectors.groupingBy(Function.identity(),Collectors.counting()))
.forEach((k,v)->empty.put(k, v)); //java coding standards variable name should be in lower case