我是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)
有人可以让我知道我在上面的代码中做错了什么吗?
答案 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());