Сбор / Подсчет в непустой карте бросает ClassCastException - PullRequest
0 голосов
/ 29 января 2019

Моя цель - хранить счетчик каждого элемента списка внутри карты.Это может быть достигнуто с помощью методов 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<>() код работает нормально.

Я что-то нарушаю, не используя пустую карту для процесса сбора?Как бы я иначе достиг своей цели с помощью потоков?

Ответы [ 3 ]

0 голосов
/ 29 января 2019

Это немного странная ошибка.В частности, используемый вами коллектор (благодаря использованию 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);

Позже они заменяют все значения карты:

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

с использованием 'финишера', определенного выше (a -> a[0]) перейти от long[] с к Long с.


Да, это немного непослушно, но вы нарушили контракт

mapFactory:поставщик, предоставляющий новую пустую карту , в которую будут вставлены результаты

, так что это также достаточно справедливо.Они берут HashMap, который во время компиляции было решено равным Map<String, Long>, и они вкладывают в него long[].Это возможно, потому что дженерики не овеществлены .Во время выполнения это просто HashMap, способный хранить любые типы ключей и значений.

0 голосов
/ 29 января 2019

Поток списка и используйте groupingBy и counting, чтобы преобразовать его в Map<String,Long>, а затем используйте foreach обновить значения в EMPTY map

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 
0 голосов
/ 29 января 2019

В качестве состояния документа для Collectors.groupingBy​(classifier, mapFactory, downstream)

Параметры:

  • классификатор - функция классификатора, отображающая элементы ввода на клавиши
  • downstream - Collector, реализующий сокращение downstream
  • mapFactory - поставщик, предоставляющий новую пустую карту , в которую будут вставлены результаты *
...