在Java Stream中执行reduce操作的异常

时间:2019-03-10 20:43:59

标签: java java-8 java-stream

我是Java 8的新手,在下面的示例中,我创建了一个Map,其键值作为String,值作为整数的ArrayList。

Map<String,List<Integer>> mapLstInteger=new HashMap<String,List<Integer>>() {
            {
                put("A",Arrays.asList(1,2,3));
                put("B",Arrays.asList(4,5,6));
                put("C",Arrays.asList(7,8,9));
            }
        };

我写了下面的代码,对每个键对arrayList元素执行求和,并试图将和值存储在单独的ArrayList中。

List<Integer> sumLst=mapLstInteger.entrySet().stream().map(e->e.getValue())
        .reduce((inputLst, outputLst)->{
            int sum=0;
            for(int count=0;count<inputLst.size();count++)
            {
                sum=sum+inputLst.get(count);
            }
            outputLst.add(sum);
            return outputLst;
        }).get();

当我尝试执行以下代码时,出现以下异常。

  

线程“ main”中的异常java.lang.UnsupportedOperationException在   java.util.AbstractList.add(AbstractList.java:148)在   java.util.AbstractList.add(AbstractList.java:108)在   com.calculation.sum.client.Client.lambda $ 1(Client.java:43)在   java.util.stream.ReduceOps $ 2ReducingSink.accept(ReduceOps.java:123)     在   java.util.stream.ReferencePipeline $ 3 $ 1.accept(ReferencePipeline.java:193)     在   java.util.HashMap $ EntrySpliterator.forEachRemaining(HashMap.java:1696)     在   java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)     在   java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)     在   java.util.stream.ReduceOps $ ReduceOp.evaluateSequential(ReduceOps.java:708)     在   java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)     在   java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:479)     在com.calculation.sum.client.Client.main(Client.java:37)

有人可以让我知道我在上面的代码中做错了什么吗?

3 个答案:

答案 0 :(得分:3)

首先,您使用的是Arrays::asList,它记录为返回由指定数组支持的固定大小列表,我认为固定大小应该告诉您什么你做错了。

比起您使用反模式来就地创建HashMap-通过创建通过HashMap扩展Map<String,List<Integer>> mapLstInteger=new HashMap<String,List<Integer>>()....的匿名内部类。

然后,您违反了reduce的规范,该规范应该一直返回一个新的Object,但是您始终将其放入outputLst

然后,当您只关心Map的值时,便要创建它-在这种情况下创建一个List<List<Integer>>

即使您的句子我写了下面的代码来针对每个键对arrayList元素求和也不正确。我只是下定决心要实现的实际目标,然后如果我是您,就尝试这样做。

答案 1 :(得分:0)

发生这种情况是因为您使用的是AbstractList产生的原始Arrays.asList
List<T>抽象实现不允许添加或删除元素。

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

但是无论如何,请回到您的问题上。您还可以通过自定义Collector获得所需的内容,在这里您可以提供自定义List<T>实现,可以是{{1} },ArrayList或其他任何更好的方式。

LinkedList

更简洁的版本

mapLstInteger.values()
             .stream()
             .collect(Collector.of(
                     () -> new ArrayList<>(),  // Supplier
                     (output, toSumList) -> {  // Accumulator
                         output.add(toSumList.stream()
                                             .mapToInt(Integer::intValue)
                                             .sum());
                     },
                     // The Combiner implementation will be called
                     // in case of a "parallel" Stream. 
                     // No need to worry about it here. 
                     // But in case, we would need to merge the partial results
                     (output, partial) -> {
                        output.addAll(partial);
                        return output;
                     }
             ));

那将正确输出mapLstInteger.values() .stream() .map(l -> l.stream().mapToInt(Integer::intValue).sum()) .collect(Collectors.toCollection(ArrayList::new));

答案 2 :(得分:0)

您应该执行以下操作:

    mapLstInteger.values().stream()
                 .flatMapToInt(list -> list.stream()
                                           .filter(Objects::nonNull)
                                           .mapToInt(Integer::intValue)).sum();

添加了过滤器,以确保在Integer为空的情况下不会得到空指针。通常,如果您被迫在流内部使用常规循环,则可能做错了什么。通过将int列表更改为int值,我们可以如上所示轻松进行求和。

最初误以为您想要总和的问题误解了,这里是针对实际问题的更新解决方案:

    mapLstInteger.values().stream()
                 .map(list -> list.stream()
                                  .filter(Objects::nonNull)
                                  .mapToInt(Integer::intValue).sum())
                                  .collect(Collectors.toList());