Jak zsumować listę liczb całkowitych strumieniami Java?


364

Chcę podsumować listę liczb całkowitych. Działa w następujący sposób, ale składnia nie wydaje się właściwa. Czy można zoptymalizować kod?

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

5
„ale składnia nie wydaje się właściwa” Co sprawia, że ​​tak myślisz? To jest zwykły idiom. Może chcesz użyć, mapToLongaby uniknąć przepełnienia, w zależności od wartości, jakie może mieć mapa.
Alexis C.,

3
@JBNizet Osobiście uważam za i -> ibardzo jasne. Cóż, tak, musisz wiedzieć, że wartość zostanie automatycznie rozpakowana, ale jest to prawdą, ponieważ Java 5 ...
Alexis C.

4
@AlexisC. jest zrozumiałe, ponieważ jest przekazywane do mapToInt () i ponieważ jestem doświadczonym programistą. Ale ja -> ja, bez kontekstu, wygląda jak noop. Integer :: intValue jest bardziej szczegółowe, ale wyraźnie wyjaśnia operację rozpakowywania.
JB Nizet

1
@JBNizet Ludzie, którzy wywołują metodę foo(int i), nie piszą za foo(myInteger.intValue());każdym razem, gdy ją wywołują (a przynajmniej nie oczekuję !!). Zgadzam się z tobą, co Integer::intValuejest bardziej wyraźne, ale myślę, że to samo dotyczy tutaj. Ludzie powinni się tego nauczyć raz, a potem skończysz :-). To nie jest tak, jakby to było jakieś magiczne zaciemnienie.
Alexis C.,

4
@JB Nizet: i -> iwygląda na to , że nie ma op, a koncepcyjnie jest to brak op. Jasne, pod maską Integer.intValue()zostaje wywołane, ale jeszcze głębiej pod maską, że metody są wprowadzane, aby stać się dokładnie tym, czego nie ma w kodzie źródłowym. Integer::intValuema tę zaletę, że nie tworzy syntetycznej metody w kodzie bajtów, ale nie jest to tym, co powinno decydować o sposobie organizacji kodu źródłowego.
Holger

Odpowiedzi:


498

To zadziała, ale i -> iwykonuje automatyczne rozpakowywanie, dlatego „wydaje się” dziwne. Oba z poniższych sposobów będą działać i lepiej wyjaśnić, co kompilator robi pod maską, używając oryginalnej składni:

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

2
Co jeśli mamy BigInteger :)?
GOXR3PLUS

13
Jedną z prostych opcji jestBigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
Matthew

158

Sugeruję jeszcze 2 opcje:

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

Drugi używa Collectors.summingInt()kolektora, jest też summingLong()kolektor, którego można użyć mapToLong.


I trzecia opcja: Java 8 wprowadza bardzo skuteczny LongAdderakumulator zaprojektowany w celu przyspieszenia podsumowania w równoległych strumieniach i środowiskach wielowątkowych. Oto przykład użycia:

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

86

Z dokumentów

Operacje redukcji Operacja redukcji (zwana także foldem) pobiera sekwencję elementów wejściowych i łączy je w jeden wynik podsumowania poprzez wielokrotne stosowanie operacji łączenia, takiej jak znalezienie sumy lub maksimum zbioru liczb lub gromadzenie elementów w lista. Klasy strumieni mają wiele form ogólnych operacji redukcji, zwanych zmniejszeniami () i collect (), a także wiele specjalistycznych form redukcji, takich jak sum (), max () lub count ().

Oczywiście takie operacje można łatwo wdrożyć jako proste sekwencyjne pętle, jak w:

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

Istnieją jednak dobre powody, aby preferować operację zmniejszania zamiast akumulacji mutacyjnej, takiej jak powyżej. Redukcja jest nie tylko „bardziej abstrakcyjna” - działa na strumieniu jako całości, a nie na pojedynczych elementach - ale odpowiednio skonstruowana operacja redukcji jest z natury możliwa do zrównoleglenia, pod warunkiem, że funkcje zastosowane do przetwarzania elementów są skojarzone i bezpaństwowiec. Na przykład, biorąc pod uwagę strumień liczb, dla którego chcemy znaleźć sumę, możemy napisać:

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

lub:

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

Te operacje redukcji mogą przebiegać bezpiecznie równolegle, prawie bez modyfikacji:

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

Tak więc dla mapy użyłbyś:

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

Lub:

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

2
To, co ma PO, jest znacznie lepsze, a także jaśniejsze. Ten kod wiązałby się z całą niechęcią do rozpakowywania i operacji bokserskich.
JB Nizet

1
@JBNizet Chyba że analiza ucieczki eliminuje boks. Musisz spróbować, aby zobaczyć, czy to możliwe.
Peter Lawrey,

6
(x, y) -> x + y musi rozpakować xiy, zsumować je, a następnie zaznaczyć wynik. I zacznij od nowa, aby dodać wynik do następnego elementu strumienia i od nowa.
JB Nizet

3
Liczba całkowita :: suma cierpi z powodu tego samego problemu. A jeśli użyjesz mapToInt (), aby mieć IntStream, wywołanie sum () na nim jest prostsze niż wywołanie redukcji ().
JB Nizet

3
Zobacz docs.oracle.com/javase/8/docs/api/java/lang/… . Dwa argumenty Integer.sum () są typu int. Tak więc dwie liczby całkowite ze strumienia muszą zostać rozpakowane, aby mogły zostać przekazane jako argumenty do metody. Metoda zwraca int, ale redukcja () przyjmuje jako argument argument BinaryOperator <Integer>, który w ten sposób zwraca liczbę całkowitą. Wynik sumy należy więc zapisać w polu „Liczba całkowita”.
JB Nizet,

28

Możesz użyć metody zmniejszania:

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

lub

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

9
Jest już taki akumulator int, to jestInteger::sum
Alex Salauyou

1
Długo wracasz, więc byłoby lepiej Long::sumniż Integer::sum.
Andrei Damian-Fekete,

16

Możesz użyć reduce()do zsumowania listy liczb całkowitych.

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

11

Możesz użyć metody Collect, aby dodać listę liczb całkowitych.

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

6

Byłby to najkrótszy sposób na podsumowanie inttypu tablica (dla longtablicy LongStream, dla doubletablicy DoubleStreami tak dalej). StreamJednak nie wszystkie pierwotne typy całkowite lub zmiennoprzecinkowe mają implementację.

IntStream.of(integers).sum();

Niestety nie mamy żadnej int-array. Więc IntStream.of()nie zadziała dla tego problemu, chyba że stworzymy coś tak upiornego:IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
Kaplan,

Nie ma potrzeby, to wystarczy integers.values().stream().mapToInt( Integer::intValue ).sum().
Sachith Dickwella,

3

Może to pomóc tym, którzy mają obiekty na liście.

Jeśli masz listę obiektów i chcesz zsumować określone pola tego obiektu, skorzystaj z poniższej listy.

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

Dzięki temu pomógł mi w jednym z moich scenariuszy
A_01

1

Zadeklarowałem listę liczb całkowitych.

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

Możesz spróbować użyć tych różnych sposobów poniżej.

Za pomocą mapToInt

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

Za pomocą summarizingInt

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

Za pomocą 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

Omówiono większość aspektów. Ale może istnieć wymóg znalezienia agregacji innych typów danych oprócz Integer, Long (dla których już istnieje specjalistyczna obsługa strumienia). Na przykład stram z BigInteger W przypadku takiego typu możemy użyć operacji zmniejszania jak

list.stream (). zmniejsz ((bigInteger1, bigInteger2) -> bigInteger1.add (bigInteger2))

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.