Як підсумувати список цілих чисел з потоками 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.