这个问题在这里已有答案:
我是Java 8的新手,在下面的示例中,我创建了一个Map,其keyvalue为String,value为ArrayList of integer。
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元素的总和,并试图将sum值存储在单独的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();
当我尝试执行以下代码时,我遇到异常。
java.util.AbstractList.add(AbstractList.java:148)中的线程“main”java.lang.UnsupportedOperationException中的异常,位于com.calculation.sum.client的java.util.AbstractList.add(AbstractList.java:108)。 java.util.stream.ReduceOps上的Client.lambda $ 1(Client.java:43)$ 2ReducingSink.accept(ReduceOps.java:123)at java.util.stream.ReferencePipeline $ 3 $ 1.accept(ReferencePipeline.java:193)at at Java.util.HashMap $ EntrySpliterator.forEachRemaining(HashMap.java:1696),java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481),java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ReduceOps $ ReduceOp.evaluateSequential(ReduceOps.java:708)at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)at java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java) :479)at com.calculation.sum.client.Client.main(Client.java:37)
任何人都可以让我知道我在上面的代码中做错了什么>
之所以发生这种情况,是因为你使用的是AbstractList
生产的原始Arrays.asList
。
List<T>
抽象实现不允许添加或删除元素。
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
但无论如何,回到你的问题。你也可以通过自定义Collector
获得你想要的东西,在那里你可以提供你的自定义List<T>
实现,无论是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));
这将正确输出[6, 15, 24]
首先你使用Arrays::asList
,它被记录为返回由指定数组支持的固定大小列表,我认为固定大小应该告诉你你做错了什么。
比你正在使用反模式创建一个HashMap
- 通过创建一个匿名的内部类,通过HashMap
扩展Map<String,List<Integer>> mapLstInteger=new HashMap<String,List<Integer>>()....
。
比,你违反了reduce
的规格,它应该一直返回一个新的对象,但你总是投入到outputLst
。
然而,当你关心的只是它的价值时,你正在创造一个Map
- 在这种情况下创造一个List<List<Integer>>
。
根据你的代码,即使是我在下面写的代码来对每个键执行arrayList元素的总和也是不正确的。我会想到我想要实现的实际事情,然后尝试去做,如果我是你。
你应该做以下事情:
mapLstInteger.values().stream()
.flatMapToInt(list -> list.stream()
.filter(Objects::nonNull)
.mapToInt(Integer::intValue)).sum();
添加了过滤器以确保在null整数的情况下不会获得空指针。作为一般规则,如果您被迫在流中使用传统循环,那么您可能做错了。通过将int列表变为int值,我们可以轻松地总结,如上所示。
最初误解了一个问题,认为你想要的总体总和是一个实际问题的更新解决方案:
mapLstInteger.values().stream()
.map(list -> list.stream()
.filter(Objects::nonNull)
.mapToInt(Integer::intValue).sum())
.collect(Collectors.toList());