2015-05-08 28 views
181

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

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

_ "ale składnia nie brzmi dobrze" _ Co sprawia, że ​​tak myślisz? To jest zwykły idiom. Może chcesz użyć 'mapToLong', aby uniknąć przepełnień, w zależności od wartości, jakie może posiadać twoja mapa. –

+3

@JBNizet Uważam, że 'i -> i' jest bardzo jasne, osobiście. Cóż, tak, musisz wiedzieć, że wartość zostanie automatycznie rozpakowana, ale to prawda, ponieważ Java 5 ... –

+2

@AlexisC. Jest to zrozumiałe, ponieważ jest przekazywane do mapToInt(), a ponieważ jestem doświadczonym programistą. Ale ja -> i, bez kontekstu, wygląda jak noop. Integer :: intValue jest bardziej szczegółowy, ale powoduje wyraźną operację rozpakowywania. –

Odpowiedz

247

to będzie działać, ale i -> i robi pewne automatyczne unboxing dlatego, że „czuje się” dziwnie.Każda z poniższych będzie działać i lepiej wyjaśnić, co kompilator robi pod wyciągiem z oryginalną składnię:

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

Od operacji rozdrobnieniu docs

Eksploatacja redukcji (zwany również krotnie) wykonuje sekwencję elementów wejściowych i łączy je w pojedynczy wynik zbiorczej przez wielokrotne zastosowanie łączący operacji takie jak znajdowanie sumy lub maksimum zbioru liczb lub gromadzenie elementów na liście. Klasy strumieni mają wiele form ogólnych operacji redukcji, zwanych zmniejszeniem() i zbieraniem(), jak również wieloma specjalistycznymi formami redukcji, takimi jak sum(), max() lub count().

Oczywiście, takie działania mogą być łatwo realizowane jako prostych pętlach sekwencyjnych, na przykład:

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

Jednakże istnieją powody, aby wolą zmniejszyć działanie na mutative akumulacji takie jak powyżej. Redukcja nie tylko jest "bardziej abstrakcyjna" - działa raczej na strumieniu jako całości niż na poszczególnych elementach - ale prawidłowo wykonana operacja zmniejszania jest z natury rzeczy możliwa do zrównoleglenia, o ile funkcja (y) używana do przetwarzania elementów są asocjacyjne i bezpaństwowców. Na przykład, biorąc pod uwagę strumień liczb, dla których 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 może bezpiecznie działać równolegle z prawie bez zmian:

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

Więc na mapie użyłbyś:

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

Lub:

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

To, co ma OP, jest znacznie lepsze, a także bardziej przejrzyste. Ten kod wiązałby się z całą masą operacji rozpakowywania i boksowania. –

+1

@JBNizet O ile analiza ucieczki nie eliminuje boksu. Musiałbyś spróbować, aby sprawdzić, czy to możliwe. –

+0

@PeterLawrey Wciąż wolałbym operować bezpośrednio na wartościach pierwotnych, wykorzystywać to, co ma OP, albo pierwsze rozwiązanie Sashy do komentowania. –

89

Proponuję jeszcze 2 opcje:

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

Drugi wykorzystuje Collectors.summingInt() kolektor, istnieje również summingLong() kolektor, które można używać w mapToLong.


i trzecie wyjście: JAVA 8 wprowadza się bardzo skuteczne LongAdder akumulatora przeznaczonego do przyspieszenia podsumowując w równoległych strumieniach i środowiskach wielu nici. Tutaj, tutaj jest przykład użycia:

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

Można wykorzystać zmniejszyć metoda:

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); 
+6

Istnieje już taki akumulator dla 'int', to jest' Integer :: sum' –

Powiązane problemy