Java - 使用forEach或Stream API执行累积和

时间:2017-02-24 09:54:52

标签: java cumulative-sum

这里是我当前的代码,用于对哈希表Map<执行累积和字符串双动和GT;

START.forEach((k,v)->{

            sum += v;
            END.put(k, sum);

        });

或者,或者

END= START.entrySet()
                .stream()
                .collect(
                        Collectors.toMap(entry -> entry.getKey(), 
                                entry -> {
                                    sum += entry.getValue();
                                    return sum;
                                }));

但我有以下错误:

Local variable sum defined in an enclosing scope must be final or effectively final

我该如何解决?

我不想像这样使用标准for循环:

Iterator it = START.entrySet().iterator();
        double sum = 0;
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry)it.next();
            String key = (String) pair.getKey();
            Double value = (Double) pair.getValue();
            sum+=value;
            END.put(date, sum);
        }



START
------------
|first | 1 |
|second| 5 |
|third | 4 |

END
|first | 1  | 
|second| 6  |
|third | 10 |

4 个答案:

答案 0 :(得分:2)

条目的顺序对累积总和非常重要。如果您使用HashMap作为实现,则无法保证地图的顺序;特别是,它不保证订单会随着时间的推移保持不变。因此,我建议您使用其他实现,例如LinkedHashMap。它使用Map接口的哈希表和链表实现,具有可预测的迭代顺序。

Map<String, Double> map = new LinkedHashMap<>();
map.put("first", 1.0);
map.put("second", 5.0);
map.put("third", 4.0);

使用原子引用来避免“最终”问题。在Java中,您不能在lambda和匿名内部类中使用非final变量。这就是为什么你得到消息“在封闭范围内定义的局部变量和必须是最终的或有效的最终”。然后,您可以将二元运算符定义为(x, y) -> x + y,因为您希望使用先前的累计和来汇总当前条目的值。

AtomicReference<Double> atomicSum = new AtomicReference<>(0.0);
map.entrySet().forEach(e -> e.setValue(
    atomicSum.accumulateAndGet(e.getValue(), (x, y) -> x + y)
));

这是最终的代码。

Map<String, Double> map = new LinkedHashMap<>();
map.put("first", 1.0);
map.put("second", 5.0);
map.put("third", 4.0);

AtomicReference<Double> atomicSum = new AtomicReference<>(0.0);
map.entrySet().forEach(e -> e.setValue(
    atomicSum.accumulateAndGet(e.getValue(), (x, y) -> x + y)
));

// tested in JUnit
assertEquals(10.0, atomicSum.get(), 0.0001);
assertEquals(1.0, map.get("first"), 0.0001);
assertEquals(6.0, map.get("second"), 0.0001);
assertEquals(10.0, map.get("third"), 0.0001);

答案 1 :(得分:1)

您需要sum成为AtomicLong并执行addAndGet而不是+=因为,正如错误所说,您需要总和为最终版本。

答案 2 :(得分:0)

我不知道,但这段代码可能对您有帮助。

List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
ints.add(3);

AtomicInteger sum = new AtomicInteger(0);
ints.stream().sequential().mapToInt(sum::addAndGet).forEach(System.out::println);

尝试在代码中修改并使用此代码段。

答案 3 :(得分:0)

您可以像这样使用java.util.concurrent.DoubleAdderCollectors#toMap

final Map<String, Double> START = new HashMap<>();
START.put("first", 1.0);
START.put("second", 5.0);
START.put("third", 4.0);

System.out.println(START.toString());

DoubleAdder sum = new DoubleAdder();
Map<String, Double> cumulativeSum = START.entrySet().stream().sequential().collect(
    Collectors.toMap(Entry::getKey, it -> { sum.add(it.getValue()); return sum.sum(); }));

System.out.println(cumulativeSum.toString());