I believe I can do next using one stream operation on listOfPricedObjects:
List<BigDecimal> myList = new ArrayList();
myList = listOfPricedObjects.stream().map(PricedObject::getPrice).collect(Collectors.toList());
BigDecimal sum = listOfPricedObjects.stream().map(PricedObject::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add)
How I can fill myList with prices and calculate sum of prices using stream one time?
Thanks
UPD: As the result I need myList filled with prices and sum variable with sum. But not with usding stream() twice for that.
You can use peek
and add to a new list
while applying the reduction
List<BigDecimal> newList = new ArrayList<>();
BigDecimal sum = list.stream()
.map(PricedObject::getPrice)
.peek(newList::add)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Please look at Tunaki answer if you interested in using a parallelStream
with a non concurrent collection which makes sense since sum is an embarrassingly parallel task.
What you want here is to collect your elements inside 2 collectors: the first one would collect into a list, and the second one would sum the price.
Since there are no such collectors in the Stream API itself, we can easily construct our own. Let's create a class ResultHolder
that will hold the result of the Stream pipeline: this is the list of decimals and the sum.
class ResultHolder {
List<BigDecimal> list = new ArrayList<>();
BigDecimal sum = BigDecimal.ZERO;
}
Finally, we can use it with:
ResultHolder resultHolder =
listOfPricedObjects.stream()
.map(PricedObject::getPrice)
.collect(
ResultHolder::new,
(r, p) -> { r.list.add(p); r.sum = r.sum.add(p); },
(r1, r2) -> { r1.list.addAll(r2.list); r1.sum = r1.sum.add(r2.sum); }
);
System.out.println(resultHolder.list);
System.out.println(resultHolder.sum);
This will work in a parallel pipeline and will keep the initial order of the list, contrary to the other answers.
While it might be reasonable to assume use cases where an arbitrary stream like listOfPricedObjects
can’t be recreated from the source and hence, only traversed once, you can safely assume that traversing the list produced via Collectors.toList()
can be traversed efficiently:
List<BigDecimal> myList = listOfPricedObjects.stream()
.map(PricedObject::getPrice).collect(Collectors.toList());
BigDecimal sum = myList.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
There is no code duplication here and any attempt to perform these two unrelated operations in one stream traversal will make the code more complicated without any benefit.
You could do a identity mapping a add the passthrough value to the list
BigDecimal sum = listOfPricedObjects.stream()
.map(o -> {
myList.add(o);
return o;})
.map(PricedObject::getPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add)
But I'd go for Sleiman Jneidi's solution using peek(), its more elegant