首页 > 解决方案 > 收集/计数到非空 Map 会引发 ClassCastException

问题描述

我的目标是将列表中每个项目的计数存储在地图中。这可以通过groupingBy()counting()方法来实现。

我的下一个约束是,对于不在 List 中的值,我仍然需要将该键映射为 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<>()代码替换就可以了。

我在收集过程中不使用空地图是否违反了某些规定?否则我将如何使用流实现我的目标?

标签: javajava-8java-stream

解决方案


这是一个有点奇怪的错误。具体来说,您正在使用的收集器(通过 using Collectors.counting)实际上正在累积到原始longs 的单个元素数组中。

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时候 a computeIfAbsent,它期望得到 along[]但是因为你已经有一个“a”的密钥,你得到的 aLong与累加器接受的类型不匹配。这就是引发异常的原因。

A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(container, t);

稍后,它们会替换所有地图值:

intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));

使用上面定义的 'finisher' ( a -> a[0]) 从long[]s 到Longs。


是的,有点淘气,但你违反了合同

mapFactory:供应商提供一个新的空地图,结果将插入其中

所以这也很公平。他们正在采用HashMapwhich 在编译时被决定为Map<String, Long>并且他们正在将long[]s 放入其中。这是可能的,因为泛型没有具体化。在运行时,它只是HashMap能够存储任何类型的键和值。


推荐阅读