sameet90
sameet90

Reputation: 133

How to reduce streams with Double datatype

I have an ArrayList of user-defined object employee as

class Employee {
    String name;
    Double age;
    Double salary
}

I am trying to reduce the above list as a stream via the following code

Double totalSalary = empList.stream().parallel().filter(x-> x.getname().equalsIgnoreCase("XYZ")).reduce(0,(subTotal, sal)-> subTotal + dp.getSalary(), DoubleExpression::add);

This does not compile and gives an error. How can I achieve my desired objective?

Upvotes: 6

Views: 2036

Answers (2)

Naman
Naman

Reputation: 31868

Ravindra's answer provides a correct solution, but to understand in detail why your solution doesn't work, consider these:

Double totalSalary = empList.stream().parallel()
        .filter(x -> x.getName().equalsIgnoreCase("XYZ"))
        // crucial aspect of mapping to the type that you want to reduce
        .map(Employee::getSalary)
        // reduction as a stage of stream pipeline execution
        .reduce(0,  // identity element for sum, but with a flaw!!
                (subTotal, sal) -> Double.sum(subTotal, sal), // accumulator to sum double values
                Double::sum // the combiner function compatible with accumulation
               );

The reason why the identity is flawed is that, because of 0, the type of subTotal in the accumulator would not be inferred as double as expected by the Double#sum method. Hence updating the code further to using Double.NaN would make it work as :

Double totalSalary = empList.stream().parallel()
        .filter(x -> x.getName().equalsIgnoreCase("XYZ"))
        .map(Employee::getSalary)
        .reduce(Double.NaN, Double::sum, Double::sum); // identity element and method reference for accumulator

Though note, this could be simply represented with the exclusion of the combiner in this case as:

Double totalSalary = empList.stream().parallel()
        .filter(x -> x.getName().equalsIgnoreCase("XYZ"))
        .map(Employee::getSalary)
        .reduce(Double.NaN, Double::sum);

and since sum eventually is an operation of primitive types, it would be much convenient to use the DoubleStream mapping which is a primitive specialization of the stream for double-valued elements.

Upvotes: 2

Ravindra Ranwala
Ravindra Ranwala

Reputation: 21124

First you have to map the Employee object into a double salary. Then only you can do the reduction step. Moreover, the reduction step takes an identity element and a binary operator. But you have passed three arguments there, which is flawed too.

double totalSalary = empList.stream()
    .filter(x -> x.getName().equalsIgnoreCase("XYZ"))
    .mapToDouble(Employee::getSalary)
    .sum();

Here's the equivalent reduction. Notice that 0 is the identity element for this reduction.

double totalSalary = empList.stream()
    .filter(x -> x.getName().equalsIgnoreCase("XYZ"))
    .mapToDouble(Employee::getSalary)
    .reduce(0, Double::sum);

Upvotes: 11

Related Questions