Як підсумувати список цілих чисел з потоками java?


364

Я хочу підсумувати список цілих чисел. Це працює так, але синтаксис не відчуває себе правильно. Чи можна оптимізувати код?

Map<String, Integer> integers;
integers.values().stream().mapToInt(i -> i).sum();

5
"але синтаксис не почуває себе правильно" Що змушує вас це думати? Це звичайна ідіома. Можливо, ви хочете використовувати, mapToLongщоб уникнути переливів, залежно від значень, які може мати ваша карта.
— Олексій К.

3
@JBNizet Мені здається i -> iдуже зрозумілим, особисто. Ну, так, вам потрібно знати, що значення буде автоматично нерозфасовано, але це правда з Java 5 ...
— Алексіс С.

4
@AlexisC. це зрозуміло, оскільки він переданий mapToInt () і тому, що я досвідчений розробник. Але i -> i, без контексту, схоже на noop. Integer :: intValue є більш детальним, але робить операцію розпакування явною.
— JB Nizet

1
@JBNizet Люди, які викликають метод foo(int i), не пишуть foo(myInteger.intValue());кожен раз, коли вони його викликають (або, принаймні, я не очікую !!). Я згоден з вами, що Integer::intValueє більш чітким, але я думаю, що це стосується і цього. Люди повинні просто засвоїти це один раз, і тоді ви закінчите :-). Це не так, якби це було якесь чарівне затуманення.
— Олексій К.

4
@JB Nizet: ну, це i -> iсхоже на неоперацію, а в принципі - це не-оп. Впевнений, що під капотом Integer.intValue()викликається, але ще глибше під кришкою, що методи отримують вказівку, щоб стати точно тим самим, що він схожий у вихідному коді. Integer::intValueмає бонусну точку не створювати синтетичний метод у байт-коді, але це не те, що повинно впливати на ваше рішення про організацію вихідного коду.
— Холгер

Відповіді:


498

Це буде спрацьовувати, але це i -> iробиться деякий автоматичний розпакування, тому це "відчувається" дивно. Будь-яке з наступного буде спрацьовувати і краще пояснити, що компілятор робить під кришкою з вашим оригінальним синтаксисом:

integers.values().stream().mapToInt(i -> i.intValue()).sum();
integers.values().stream().mapToInt(Integer::intValue).sum();

2
Що робити, якщо у нас є BigInteger :)?
— GOXR3PLUS

13
Один простий варіантBigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
— Матвій

158

Пропоную ще 2 варіанти:

integers.values().stream().mapToInt(Integer::intValue).sum();
integers.values().stream().collect(Collectors.summingInt(Integer::intValue));

У другому використовується Collectors.summingInt()колектор, є також summingLong()колектор, яким ви користуєтесь mapToLong.


І третій варіант: Java 8 представляє дуже ефективний LongAdderакумулятор, розроблений для прискорення підбиття підсумків у паралельних потоках та багатопотокових середовищах. Ось приклад використання:

LongAdder a = new LongAdder();
map.values().parallelStream().forEach(a::add);
sum = a.intValue();

86

Від док

Операції скорочення Операція скорочення (також звана складкою) приймає послідовність вхідних елементів і об'єднує їх в єдиний підсумковий результат шляхом повторного застосування комбінуючої операції, наприклад, знаходження суми або максимуму набору чисел або накопичення елементів у Лист. Класи потоків мають безліч форм загальних операцій скорочення, званих зменшити () і збирати (), а також безліч спеціалізованих форм зменшення, таких як сума (), макс () або кількість ().

Звичайно, такі операції можна легко реалізувати як прості послідовні петлі, як у:

int sum = 0;
for (int x : numbers) {
   sum += x;
}

Однак є вагомі причини віддати перевагу операції зменшення над мутаційним накопиченням, як зазначено вище. Мало того, що зменшення є "більш абстрактним" - воно діє на потоці в цілому, а не на окремих елементах, - але правильно побудована операція зменшення за своєю суттю є паралельною, доки функції, які використовуються для обробки елементів, є асоціативними та без громадянства. Наприклад, задавши потік чисел, за якими ми хочемо знайти суму, ми можемо написати:

int sum = numbers.stream().reduce(0, (x,y) -> x+y);

або:

int sum = numbers.stream().reduce(0, Integer::sum);

Ці операції відновлення можуть безпечно виконуватись паралельно, майже не змінюючи:

int sum = numbers.parallelStream().reduce(0, Integer::sum);

Отже, для карти ви б використовували:

integers.values().stream().mapToInt(i -> i).reduce(0, (x,y) -> x+y);

Або:

integers.values().stream().reduce(0, Integer::sum);

2
Те, що ОП має набагато краще, а також зрозуміліше. Цей код передбачає цілу групу операцій розпакування та боксу.
— JB Nizet

1
@JBNizet Якщо аналіз втечі не усуне бокс. Вам доведеться спробувати, щоб побачити, чи може це.
— Пітер Лорі

6
(x, y) -> x + y потрібно розблокувати x і y, підсумувати їх, а потім встановити вікно результату. І почніть знову додавати результат із наступним елементом потоку, і знову і знову.
— JB Nizet

3
Ціла задача: сума страждає від тієї ж проблеми. І якщо ви використовуєте mapToInt (), щоб мати IntStream, виклик суми () на ньому є більш простим, ніж виклик скарати ().
— JB Nizet

3
Див. Docs.oracle.com/javase/8/docs/api/java/lang/… . Два аргументи Integer.sum () мають тип int. Тож два цілих числа з потоку повинні бути нерозподіленими та передані як аргументи методу. Метод повертає int, але Reduct () приймає BinaryOperator <Integer> як аргумент, який, таким чином, повертає Integer. Таким чином, результат суми повинен бути зафіксований у Integer.
— JB Nizet

28

Можна використовувати метод зменшення:

long sum = result.stream().map(e -> e.getCreditAmount()).reduce(0L, (x, y) -> x + y);

або

long sum = result.stream().map(e -> e.getCreditAmount()).reduce(0L, Integer::sum);

9
Тут вже є такий акумулятор int, цеInteger::sum
— Алекс Салауйо,

1
Ви довго повертаєтесь, тож було б краще, Long::sumніж Integer::sum.
— Андрій Даміан-Фекете

16

Ви можете використовувати reduce()для підсумовування списку цілих чисел.

int sum = integers.values().stream().reduce(0, Integer::sum);

11

Ви можете використовувати метод збирання, щоб додати список цілих чисел.

List<Integer> list = Arrays.asList(2, 4, 5, 6);
int sum = list.stream().collect(Collectors.summingInt(Integer::intValue));

6

Це був би найкоротший спосіб підбити підсумок intмасиву типів (для longмасиву LongStream, для doubleмасиву DoubleStreamтощо). Не всі типові цілісні чи цілі з плаваючою точкою мають Streamреалізацію.

IntStream.of(integers).sum();

На жаль, у нас немає жодного внутрішнього масиву. Тож IntStream.of()не вдасться вирішити цю проблему, якщо тільки ми не зробимо щось моторошне на кшталт цього:IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
— Каплан,

Не потрібно, цього буде достатньо integers.values().stream().mapToInt( Integer::intValue ).sum().
— Сахіт Діквелла

3

Нехай це допоможе тим, у кого об’єкти в списку.

Якщо у вас є список об'єктів і хочете підсумовувати конкретні поля цього об’єкта, скористайтеся нижче.

List<ResultSom> somList = MyUtil.getResultSom();
BigDecimal result= somList.stream().map(ResultSom::getNetto).reduce(
                                             BigDecimal.ZERO, BigDecimal::add);

Дякую, це допомогло мені в одному з моїх
— сценіоріонів

1

Я оголосив список цілих чисел.

ArrayList<Integer> numberList = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));

Ви можете спробувати скористатися цими різними способами нижче.

Використання mapToInt

int sum = numberList.stream().mapToInt(Integer::intValue).sum();

Використання summarizingInt

int sum = numberList.stream().collect(Collectors.summarizingInt(Integer::intValue)).getSum();

Використання reduce

int sum = numberList.stream().reduce(Integer::sum).get().intValue();

-1
class Pojo{
    int num;

    public Pojo(int num) {
        super();
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

List<Pojo> list = new ArrayList<Pojo>();
            list.add(new Pojo(1));
            list.add(new Pojo(5));
            list.add(new Pojo(3));
            list.add(new Pojo(4));
            list.add(new Pojo(5));

            int totalSum = list.stream().mapToInt(pojo -> pojo.getNum()).sum();
            System.out.println(totalSum);

-1

Більшість аспектів висвітлюються. Але може виникнути вимога знайти агрегацію інших типів даних, окрім Integer, Long (для якої вже існує спеціалізована підтримка потоку). Наприклад, наприклад, страйм з BigInteger Для такого типу ми можемо використовувати операцію зменшення на зразок

list.stream (). зменшити ((bigInteger1, bigInteger2) -> bigInteger1.add (bigInteger2))

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.