Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to collect two sum from a stream in Java 8

class Stock{
   double profit;
   double profitPercentage;
   public double getProfit(){
      return profit;
   }
   public double getProfitPercentage(){
      return profitPercentage;
   }
}
List<Stock> stocks = getAllStocks();
stocks.stream.collect(Collectors.summarizingDouble(Stock:getProfit)).getSum();
stocks.stream.collect(Collectors.summarizingDouble(Stock:getProfitPercentage)).getSum();

I could not find out way to do in single pass of stream. Any help or pointer would be good.

like image 643
Senthil Kumar Vaithiyanathan Avatar asked Oct 21 '25 22:10

Senthil Kumar Vaithiyanathan


1 Answers

The straight-forward way is to create a custom collector class.

public class StockStatistics {

    private DoubleSummaryStatistics profitStat = new DoubleSummaryStatistics();
    private DoubleSummaryStatistics profitPercentageStat = new DoubleSummaryStatistics();

    public void accept(Stock stock) {
        profitStat.accept(stock.getProfit());
        profitPercentageStat.accept(stock.getProfitPercentage());
    }

    public StockStatistics combine(StockStatistics other) {
        profitStat.combine(other.profitStat);
        profitPercentageStat.combine(other.profitPercentageStat);
        return this;
    }

    public static Collector<Stock, ?, StockStatistics> collector() {
        return Collector.of(StockStatistics::new, StockStatistics::accept, StockStatistics::combine);
    }

    public DoubleSummaryStatistics getProfitStat() {
        return profitStat;
    }

    public DoubleSummaryStatistics getProfitPercentageStat() {
        return profitPercentageStat;
    }

}

This class serves as a wrapper around two DoubleSummaryStatistics. It delegates to them each time an element is accepted. In your case, since you're only interested in the sum, you could even use a Collectors.summingDouble instead of DoubleSummaryStatistics. Also, it returns the two statistics with getProfitStat and getProfitPercentageStat; alternatively, you could add a finisher operation that would return a double[] containing only both sums.

Then, you can use

StockStatistics stats = stocks.stream().collect(StockStatistics.collector());
System.out.println(stats.getProfitStat().getSum());
System.out.println(stats.getProfitPercentageStat().getSum());

A more generic way is to create a collector capable of pairing other collectors. You can use the pairing collector written in this answer and, also available in the StreamEx library.

double[] sums = stocks.stream().collect(MoreCollectors.pairing(
    Collectors.summingDouble(Stock::getProfit),
    Collectors.summingDouble(Stock::getProfitPercentage),
    (sum1, sum2) -> new double[] { sum1, sum2 }
));

The sum of the profit will be in sums[0] and the sum of the profit percentage will be in sums[1]. In this snippet, only the sums are kept and not the whole stats.

like image 184
Tunaki Avatar answered Oct 24 '25 12:10

Tunaki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!