收集/计数到非空Map中会引发ClassCastException

时间:2019-01-29 16:54:51

标签: java java-8 java-stream

我的目标是将列表中每个项目的计数存储在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进行收集过程违反了某些规定吗?否则我将如何使用流实现我的目标?

3 个答案:

答案 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)

流式传输列表,并使用groupingBycounting将其转换为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