Reputation: 907
If I have a java stream of e.g. numbers, is it possible to calculate e.g. the sums up to that number (by adding the number to the "previously" calculated sum)?
e.g. (1, 2, 3, 5, 7, 9, 0) --> (1, 3, 6, 11, 18, 27, 27)
Upvotes: 3
Views: 787
Reputation: 1503
A simple solution using Streams is to use reducing
. This solution is not stateless, though.
var inputList = List.of(1, 2, 3, 5, 7, 9, 0);
var result = new ArrayList<>();
var unused = inputList.stream().collect(reducing(0, (a, b) -> {
result.add(a + b);
return a + b;
}));
System.out.println(result); // [1, 3, 6, 11, 18, 27, 27]
A few points:
parallelPrefix
solution in Faeemazaz Bhanej answer should be the preferred solutionUpvotes: 1
Reputation: 2396
You have to use parallelPrefix for sum of array in java stream.
Integer[] arr = {1, 2, 3, 5, 7, 9, 0};
Arrays.parallelPrefix(arr, (x, y) -> x + y);
System.out.println(Arrays.toString(arr));
You have to use AtomicInteger for ArrayList
List<Integer> list = new ArrayList<>();
list.addAll(Arrays.asList(1, 2, 3, 5, 7, 9, 0));
AtomicInteger ai = new AtomicInteger();
List<Integer> sumOfList = list.stream()
.map(ai::addAndGet)
.collect(Collectors.toList());
System.out.println(sumOfList);
Upvotes: 4
Reputation: 10834
Here is a proof of concept building on this answer. It implements a fully stateless solution with O(n) time complexity that also works with parallel streams.
record CumSum(Integer acc, List<Integer> sums) {
public CumSum() {
this(0, List.of());
}
public CumSum collect(Integer n) {
return new CumSum(acc + n, concat(sums, Stream.of(acc + n)));
}
public CumSum combine(CumSum cumSum) {
return new CumSum(acc + cumSum.acc, concat(sums, cumSum.sums.stream().map(n -> acc + n)));
}
private static List<Integer> concat(List<Integer> sums, Stream<Integer> acc) {
return Stream.concat(sums.stream(), acc).toList();
}
}
var list1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
var list2 = list1.stream().parallel()
.reduce(new CumSum(), CumSum::collect, CumSum::combine)
.sums;
System.out.println(list2);
The output is
[1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
regardless of using a parallel stream or not.
Note that due to the lack of a true immutable List in Java, CumSum
jumps back and forth from List
and Stream
, which complicates this implementation unnecessarily.
Also the separate tracking in acc
could be replaced by just looking at the last element in sums
. For more clarity I chose not to do that.
Upvotes: 0
Reputation: 99
yes, you can use a custom collector to do that. Although the "stream" part would have nothing to do.
It would look like list.stream().collect( new CustomSequentialCollector() )
Upvotes: -1