java8 stream sum multiple

2019-04-08 18:44发布

问题:

I am curious, how to sum up multiple variables in a java8 stream.

Integer wCPU = 0;
Double wnetwork = 0.0;
Double wMem = 0.0;

this.slaContractList.forEach(sla -> {
    wCPU += sla.getNumberOfCPUs();
    wnetwork += sla.getNetworkBandwith();
    wMem += sla.getMemory();
});

However, this does not compile as the variable in the lambda expression should be final.

回答1:

Assuming slaContractList is a list of SlaContract objects, and it has constructor SlaContract(numberOfCPUs, networkBandwith, memory) you can:

SlaContract sumContract = slaContractList.stream()
    .reduce(new SlaContract(0, 0.0, 0.0), (sla1, sla2) -> {
         return new SlaContract(sla1.getNumberOfCPUs() + sla2.getNumberOfCPUs(), sla1.getworkBandwith() + sla2.getworkBandwith(), sla1.getMemory() + sla2.getMemory());
    });

Double wnetwork = sumContract.getworkBandwith();
Double wMem = sumContract.getMemory();
Integer wCPU = sumContract.getNumberOfCPUs();

The same solution, but for simple class:

Point sumPoint = pointsList.stream()
    .reduce(new Point(0, 0), (p1, p2) -> {
         return new Point(p1.x + p2.x, p1.y + p2.y);
    });


回答2:

Try to use Stream.reduce and Stream.sum:

Double wnetwork = slaContractList.stream()
            .mapToDouble(sla -> sla.getNetworkBandwith())
            .sum();

Double wMem = slaContractList.stream()
            .mapToDouble(sla -> sla.getMemory())
            .sum();

Integer wCPU = slaContractList.stream()
            .mapToInt(sla -> sla.getNumberOfCPUs())
            .sum();

See https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html

The advantage of using stream is option of use parallelStream() instead of stream(). In some situations it can be more efficient than simple loop.



回答3:

Just to do a sum, i would use the sum operation of streams just like in Łukasz answer, but, for a more general solution for resolving the "final problem" you can use the classes of java.util.concurrent.atomic. It is intented to be used in stream and is thread-safe, so could be used in a parallel stream.

AtomicInteger wCPU = new AtomicInteger();
DoubleAccumulator wnetwork = new DoubleAccumulator(Double::sum,0.d);
DoubleAccumulator wMem = new DoubleAccumulator(Double::sum,0.d);

this.slaContractList.forEach(sla -> {
    wCPU.addAndGet(sla.getNumberOfCPUs());
    wnetwork.accumulate(sla.getNetworkBandwith());
    wMem.accumulate(sla.getMemory());
});

Now you see that there are 2 kind of implementation: the Accumulator and the Atomic, the choice between these 2 is another question:

java 8 : Are LongAdder and LongAccumulator preferred to AtomicLong?



回答4:

I would do a simple hack like this:

    Integer[] wCPU = new Integer[1];
    Double[] wnetwork = new Double[1];
    Double[] wMem = new Double[1];

    this.slaContractList.forEach(sla -> {
        wCPU[0] += sla.getNumberOfCPUs();
        wnetwork[0] += sla.getNetworkBandwith();
        wMem[0] += sla.getMemory();
    });

It's optional to have the final keyword for both , as in Java 8 they have introduced the effectively final concept. It means, you have assigned only once.