Is mapToDouble () really necessary to summarize List <Double> with Java 8 threads?
As far as I can tell, a way to sum a List<Double> using Java 8 threads:
List<Double> vals = . . . ; double sum = vals.stream().mapToDouble(Double::doubleValue).sum(); It seems to me that mapToDouble(Double::doubleValue) seems cool - it's just a ceremonial ceremony that lambdas and streams should have done without.
Best practices tell us that we prefer List instances for arrays, and yet for this kind of summation, arrays seem cleaner:
double[] vals = . . . ; double sum = Arrays.stream(vals).sum(); Of course, this can be done:
List<Double> vals = . . . ; double sum = vals.stream().reduce(0.0, (i,j) -> i+j); But this reduce(....) much longer than sum() .
I understand that this is due to how threads need to be modified around Java objects that are not objects, but did I miss something? Is there a way to compress autoboxing to make it shorter? Or is it just the current state of art?
Update - Answer Digest
Below is a digest of answers. While I have a resume here, I urge the reader to fully familiarize themselves with the answers.
@dasblinkenlight explains that some kind of unboxing will always be necessary because of decisions made back in the history of Java, in particular, how generics were implemented and their relationship with non-elementary primitives. He notes that it is theoretically possible for the compiler to be able to perform unboxing and allow shorter code, but this is not yet implemented.
@Holger shows a solution that is very close to the expressiveness I asked about:
double sum = vals.stream().reduce(0.0, Double::sum); I did not know about the new static Double.sum() method. Added with 1.8 seems to be intended for the very purpose that I described. I also found Double.min() and Double.max() . In the future, I will definitely use this idiom for such operations on List<Double> , etc.
For me,
mapToDouble(Double::doubleValue)seems [that] lambdas and threads should do without.
The need to use mapToDouble is a consequence of the decision to introduce generics by erasing a type that essentially closes the door whenever possible using primitives inside generics. It was this decision that required the creation of the DoubleStream , IntStream and LongStream family of classes to provide stream-based decompression.
Is there a way to compress autoboxing to make it shorter? Or is it just the current state of art?
Unfortunately, not at this time: although it is theoretically possible for the compiler to understand that Stream<Double> can be converted to DoubleStream implicitly, just as primitives will be unpacked, this has not been done.
As for the array-based solution, it is the most efficient of the three. However, it is not as flexible as the other two: one with mapToDouble allows mapToDouble to summarize any attribute of a custom class, while the latter allows you to perform other types of aggregation.
reduce(....)much longer thansum()
I agree, this approach is worse than mapToDouble in terms of readability.
Is there a way to compress autoboxing to make it shorter?
Yes there is. You can simply write:
double sum = vals.stream().mapToDouble(d->d).sum(); This makes unboxing implicit, but certainly does not increase efficiency.
Because List is boxed, unboxing is inevitable. Alternative approach:
double sum = vals.stream().reduce(0.0, Double::sum); It does not make mapToDouble , but still allows you to read the code as "... sum".
Here is another way to do this. If you need only the sum, average, min, max, etc. In the Double , Integer or Long list, you can use one of the available Collectors , for example:
List<Double> doubles = Arrays.asList(3.14, 5.15, 4.12, 6.); System.out.println( doubles.stream() .collect(Collectors.summingDouble(d -> d)) ); will print 18.41
Note that the method name is summingDouble , there is another method called summaryizing double that returns DoubleSummaryStatistics , containing all the basic results of mathematical calculations.