Подтвердить что ты не робот

Действительно ли mapToDouble() необходим для суммирования List <Double> с потоками Java 8?

Насколько я могу судить, способ суммирования List<Double> с использованием потоков Java 8:

List<Double> vals = . . . ;
double sum = vals.stream().mapToDouble(Double::doubleValue).sum();

Мне кажется, что mapToDouble(Double::doubleValue) кажется крутым - это просто церемониальная церемония, которую лямбды и ручьи должны были обойтись без.

Лучшие практики говорят нам, что мы предпочитаем экземпляры List для массивов, и все же для такого рода суммирования массивы кажутся более чистыми:

double[] vals = . . . ;
double sum = Arrays.stream(vals).sum();

Конечно, это можно сделать:

List<Double> vals = . . . ;
double sum = vals.stream().reduce(0.0, (i,j) -> i+j);

Но это reduce(....) намного длиннее sum().

Я понимаю, что это связано с тем, как потоки необходимо модифицировать вокруг Java-объектов, не являющихся объектами, но все-таки я что-то упустил? Есть ли способ сжать автобоксинг, чтобы сделать это короче? Или это только текущее состояние искусства?


Обновление - дайджест ответов

Ниже приведен дайджест ответов. В то время как у меня есть резюме здесь, я настоятельно призываю читателя полностью ознакомиться с ответами.

@dasblinkenlight объясняет, что какой-то unboxing всегда будет необходим из-за решений, принятых еще в истории Java, в частности, в том, как были реализованы дженерики и их связь с неэлементными примитивами. Он замечает, что теоретически возможно, чтобы компилятор смог выполнить unboxing и разрешить более короткий код, но это еще не реализовано.

@Holger показывает решение, которое очень близко к выразительности, о которой я спрашивал:

double sum = vals.stream().reduce(0.0, Double::sum);

Я не знал о новом статическом методе Double.sum(). Добавленный с 1.8, кажется, предназначен для самой цели, которую я описывал. Я также нашел Double.min() и Double.max(). В будущем я обязательно буду использовать эту идиому для таких операций на List<Double> и т.д.

4b9b3361

Ответ 1

Для меня mapToDouble(Double::doubleValue) кажется [что] лямбды и потоки должны были обойтись без.

Необходимость использования mapToDouble является следствием решения о внедрении дженериков с помощью стирания типа, по сути закрывающего дверь при любой возможности использования примитивов внутри дженериков. Именно это решение потребовало создать семейство классов DoubleStream, IntStream и LongStream - для обеспечения распаковки на основе потоков.

Есть ли способ сжать автобоксинг, чтобы сделать это короче? Или это только текущее состояние искусства?

К сожалению, не в это время: хотя теоретически возможно, чтобы компилятор понял, что Stream<Double> может быть преобразован в DoubleStream неявно, так же, как примитивы будут распакованы, это не было сделано.

Что касается решения на основе массива, оно является наиболее эффективным из трех. Однако он не так гибкий, как два других: один с mapToDouble позволяет суммировать любой атрибут настраиваемого класса, в то время как последний позволяет выполнять другие типы агрегации.

reduce(....) намного длиннее sum()

Я согласен, этот подход хуже, чем mapToDouble с точки зрения удобочитаемости.

Ответ 2

Есть ли способ сжать автобоксинг, чтобы сделать это короче?

Да, есть. Вы можете просто написать:

double sum = vals.stream().mapToDouble(d->d).sum();

Это делает unboxing неявным, но, конечно, не повышает эффективность.

Поскольку List помещается в коробку, unboxing неизбежно. Альтернативный подход:

double sum = vals.stream().reduce(0.0, Double::sum);

Он не делает mapToDouble, но все же позволяет читать код как "... sum".

Ответ 3

Вот еще один способ сделать это. Если вам нужна только сумма, среднее значение, мин, макс и т.д. В списке Double, Integer или Long, вы можете использовать один из доступных Collectors, например:

List<Double> doubles = Arrays.asList(3.14, 5.15, 4.12, 6.);
System.out.println(
        doubles.stream()
                .collect(Collectors.summingDouble(d -> d))
);

будет печатать 18.41

Обратите внимание, что имя метода summingDouble, есть еще один метод под названием summaryizing double, который возвращает DoubleSummaryStatistics, содержащий все основные результаты математических вычислений.