Como somar uma lista de números inteiros com fluxos java?

365

Eu quero somar uma lista de números inteiros. Funciona da seguinte maneira, mas a sintaxe não parece correta. O código pode ser otimizado?

Map<String, Integer> integers;
integers.values().stream().mapToInt(i -> i).sum();
som do membro
fonte
5
"mas a sintaxe não parece certa" O que faz você pensar isso? Este é o idioma usual. Talvez você queira usar mapToLongpara evitar estouros, dependendo dos valores que seu mapa possa ter.
Alexis C.
3
@JBNizet Acho i -> imuito claro, pessoalmente. Bem, sim, você precisa saber que o valor será automaticamente fora da caixa, mas é verdade, pois Java 5 ...
Alexis C.
4
@AlexisC. é compreensível porque passou para mapToInt () e porque sou um desenvolvedor experiente. Mas i -> i, sem contexto, parece um noop. Integer :: intValue é mais detalhado, mas torna explícita a operação de unboxing.
JB Nizet
11
@JBNizet As pessoas que chamam o método foo(int i)não escrevem foo(myInteger.intValue());cada vez que o chamam (ou pelo menos eu espero que não !!). Concordo com você que Integer::intValueé mais explícito, mas acho que o mesmo se aplica aqui. As pessoas deveriam aprender uma vez e então você terminou :-). Não é como se houvesse alguma ofuscação mágica.
Alexis C.
4
@ JB Nizet: bem, i -> iparece um no-op e conceitualmente, é um no-op. Claro, sob o capô Integer.intValue()é chamado, mas ainda mais profundo, os métodos são incorporados para se tornar exatamente o não operacional que parece no código-fonte. Integer::intValuetem o ponto de bônus de não criar um método sintético no código de bytes, mas não é o que deve orientar sua decisão de como organizar seu código-fonte.
Holger

Respostas:

499

Isso funcionará, mas o i -> iestá fazendo algum desembalagem automática e é por isso que "parece" estranho. Qualquer um dos seguintes procedimentos funcionará e melhor explicará o que o compilador está fazendo sob o capô com sua sintaxe original:

integers.values().stream().mapToInt(i -> i.intValue()).sum();
integers.values().stream().mapToInt(Integer::intValue).sum();
Necreaux
fonte
2
E se tivermos um BigInteger :)?
GOXR3PLUS 15/01/19
13
Uma opção simples éBigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
Matthew
158

Sugiro mais 2 opções:

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

O segundo usa Collectors.summingInt()coletor, há também um summingLong()coletor com o qual você usaria mapToLong.


E uma terceira opção: o Java 8 apresenta um LongAdderacumulador muito eficaz, projetado para acelerar o resumo em fluxos paralelos e ambientes multiencadeados. Aqui, aqui está um exemplo de uso:

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

Dos documentos

Operações de redução Uma operação de redução (também chamada de dobra) pega uma sequência de elementos de entrada e os combina em um único resultado resumido pela aplicação repetida de uma operação combinada, como encontrar a soma ou o máximo de um conjunto de números ou acumular elementos em uma lista. As classes de fluxos possuem várias formas de operações gerais de redução, chamadas reduzir () e coletar (), bem como várias formas especializadas de redução, como sum (), max () ou count ().

Obviamente, essas operações podem ser prontamente implementadas como loops sequenciais simples, como em:

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

No entanto, existem boas razões para preferir uma operação de redução a uma acumulação mutativa, como a acima. Não é apenas uma redução "mais abstrata" - ela opera no fluxo como um todo, em vez de elementos individuais - mas uma operação de redução adequadamente construída é inerentemente paralelelizável, desde que as funções usadas para processar os elementos sejam associativas e apátrida. Por exemplo, dado um fluxo de números para o qual queremos encontrar a soma, podemos escrever:

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

ou:

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

Essas operações de redução podem ser executadas com segurança em paralelo com quase nenhuma modificação:

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

Então, para um mapa você usaria:

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

Ou:

integers.values().stream().reduce(0, Integer::sum);
J Atkin
fonte
2
O que o OP tem é muito melhor e também mais claro. Esse código envolveria uma série de operações de unboxing e boxing.
JB Nizet
11
@JBNizet A menos que a análise de escape elimine o boxe. Você teria que tentar para ver se pode.
Peter Peterrey
6
(x, y) -> x + y precisa desmarcar x e y, soma-los e, em seguida, encaixotar o resultado. E comece novamente para adicionar o resultado com o próximo elemento do fluxo e novamente e novamente.
JB Nizet
3
Integer :: sum sofre do mesmo problema. E se você usar mapToInt () para ter um IntStream, chamar sum () é mais direto do que chamar reduzir ().
JB Nizet
3
Consulte docs.oracle.com/javase/8/docs/api/java/lang/… . Os dois argumentos de Integer.sum () são do tipo int. Portanto, os dois números inteiros do fluxo devem ser descompactados para serem passados ​​como argumentos para o método O método retorna um int, mas o reduz () recebe um BinaryOperator <Integer> como argumento, que retorna um número inteiro. Portanto, o resultado da soma deve ser encaixotado como Inteiro.
JB Nizet
28

Você pode usar o método de redução:

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

ou

long sum = result.stream().map(e -> e.getCreditAmount()).reduce(0L, Integer::sum);
Saeed Zarinfam
fonte
9
Já existe esse acumulador para int, éInteger::sum
Alex Salauyou
11
Você está retornando muito tempo, então seria melhor Long::sumque Integer::sum.
Andrei Damian-Fekete
16

Você pode usar reduce()para somar uma lista de números inteiros.

int sum = integers.values().stream().reduce(0, Integer::sum);
Johnson Abraham
fonte
11

Você pode usar o método collect para adicionar uma lista de números inteiros.

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

Essa seria a maneira mais curta de resumir o inttipo de matriz (para longmatriz LongStream, doublematriz DoubleStreame assim por diante). StreamPorém, nem todos os tipos de números inteiros primitivos ou de ponto flutuante têm a implementação.

IntStream.of(integers).sum();
Sachith Dickwella
fonte
Infelizmente não temos nenhum array interno. Então, IntStream.of()não vai funcionar para este problema, a menos que nós estamos fazendo algo assustador como este:IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
Kaplan
Não há necessidade, isso seria suficiente integers.values().stream().mapToInt( Integer::intValue ).sum().
Sachith Dickwella
3

Que isso ajude aqueles que têm objetos na lista.

Se você possui uma lista de objetos e deseja somar campos específicos desse objeto, use o abaixo.

List<ResultSom> somList = MyUtil.getResultSom();
BigDecimal result= somList.stream().map(ResultSom::getNetto).reduce(
                                             BigDecimal.ZERO, BigDecimal::add);
itro
fonte
Graças ajudou-me em um dos meus scenerio
A_01
1

Eu declarei uma lista de números inteiros.

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

Você pode tentar usar estas maneiras diferentes abaixo.

Usando mapToInt

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

Usando summarizingInt

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

Usando reduce

int sum = numberList.stream().reduce(Integer::sum).get().intValue();
JDGuide
fonte
-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);
Ranjit Soni
fonte
-1

A maioria dos aspectos é coberta. Mas pode haver um requisito para encontrar a agregação de outros tipos de dados além de Inteiro, Longo (para o qual já existe suporte especializado a fluxos). Por exemplo, stram com BigInteger Para esse tipo, podemos usar operações de redução como

Você pode usar o seguinte comando: list.stream ().

ManojG
fonte