Java 8 Stream add elements to list and sum

有些话、适合烂在心里 提交于 2019-12-01 05:34:15

问题


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.


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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



来源:https://stackoverflow.com/questions/37187541/java-8-stream-add-elements-to-list-and-sum

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!