Java akışlarıyla tamsayıların listesi nasıl toplanır?


364

Tamsayıların bir listesini toplamak istiyorum. Aşağıdaki gibi çalışır, ancak sözdizimi doğru hissetmez. Kod optimize edilebilir mi?

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

5
"ancak sözdizimi doğru gelmiyor" Bunu düşündüren nedir? Bu olağan deyimdir. mapToLongHaritanızın sahip olabileceği değerlere bağlı olarak taşmaları önlemek için kullanmak isteyebilirsiniz .
Alexis C.

3
@JBNizet Şahsen i -> içok net buluyorum . Evet, değerin otomatik olarak kutudan çıkarılacağını bilmeniz gerekiyor, ancak Java 5'ten beri doğru ...
Alexis C.

4
@AlexisC. mapToInt () 'a geçtiği ve deneyimli bir geliştirici olduğum için anlaşılabilir. Ama i -> i, bağlam olmadan, bir noop gibi görünüyor. Integer :: intValue daha ayrıntılıdır, ancak kutudan çıkarma işlemini açık hale getirir.
JB Nizet

1
@JBNizet Yöntemi çağıran insanlar foo(int i)bunu foo(myInteger.intValue());her çağırdıklarında yazmazlar (ya da en azından beklemiyorum !!). Sana katılıyorum, bu Integer::intValuedaha açık ama aynı şey burada da geçerli. İnsanlar sadece bir kez öğrenmeli ve sonra bitirdiniz :-). Biraz sihirli bir şaşkınlık gibi değil.
Alexis C.

4
@JB Nizet: iyi, i -> ino-op ve conceptionally gibi görünüyor, bu ise no-op. Elbette, başlık altında Integer.intValue()denir, ancak başlık altında daha da derin, bu yöntemler tam olarak kaynak kodunda göründüğü no-op haline gelir. Integer::intValue, bayt kodunda sentetik bir yöntem oluşturmamanın bonus noktasına sahiptir, ancak kaynak kodunuzu nasıl düzenleyeceğinize karar vermenizi gerektirmez.
Holger

Yanıtlar:


498

Bu işe yarayacak, ancak i -> ibazı otomatik kutu açma yapıyor, bu yüzden garip "hissediyor". Aşağıdakilerden biri işe yarayacak ve derleyicinin orijinal sözdiziminizle kaputun altında ne yaptığını daha iyi açıklayacaktır:

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

2
Bir BigInteger'imiz varsa ne olur :)?
GOXR3PLUS

13
Basit bir seçenekBigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
Matthew

158

2 seçenek daha öneririm:

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

İkincisi Collectors.summingInt()toplayıcı kullanıyor , summingLong()birlikte kullanacağınız bir toplayıcı da var mapToLong.


Üçüncü bir seçenek: Java 8 LongAdder, paralel akışlarda ve çok iş parçacıklı ortamlarda özetlemeyi hızlandırmak için tasarlanmış çok etkili bir akümülatör sunar. İşte size bir örnek kullanım:

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

86

Gönderen docs

Küçültme işlemleri Küçültme işlemi (katlama olarak da adlandırılır) bir dizi girdi öğesini alır ve bir dizi sayının toplamını veya maksimumunu bulma veya bir liste. Streams sınıfları, genel olarak azaltma () ve collect () adında birden çok genel azaltma işlemi biçiminin yanı sıra sum (), max () veya count () gibi birden çok özel azaltma biçimine sahiptir.

Tabii ki, bu tür operasyonlar, aşağıdaki gibi basit sıralı döngüler olarak kolayca uygulanabilir:

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

Bununla birlikte, yukarıdaki gibi mutasyona uğramış bir birikim yerine indirgeme işlemini tercih etmek için iyi nedenler vardır. Bir azaltma "daha soyut" değildir - akış üzerinde ayrı ayrı elemanlardan ziyade bir bütün olarak çalışır - aynı zamanda, elemanları işlemek için kullanılan fonksiyon (lar) birleştirici olduğu sürece, düzgün yapılandırılmış bir azaltma işlemi doğal olarak paralelleştirilebilir. ve vatansız. Örneğin, toplamını bulmak istediğimiz bir sayı akışı göz önüne alındığında, şunları yazabiliriz:

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

veya:

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

Bu azaltma işlemleri, neredeyse hiçbir değişiklik yapılmadan paralel olarak güvenli bir şekilde çalışabilir:

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

Yani, bir harita için şunları kullanırsınız:

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

Veya:

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

2
OP'nin sahip olduğu şey çok daha iyi ve aynı zamanda daha net. Bu kod, bir sürü boks ve boks operasyonu içerir.
JB Nizet

1
@JBNizet Kaçış analizi boksu ortadan kaldırmazsa. Bunu yapıp yapamayacağını görmek için denemeniz gerekir.
Peter Lawrey

6
(x, y) -> x + y, x ve y'nin kutularını açmalı, toplamalı ve ardından sonucu kutulamalıdır. Ve sonucu akışın bir sonraki öğesiyle eklemek için tekrar başlayın ve tekrar tekrar.
JB Nizet

3
Integer :: sum aynı problemden muzdariptir. Ve bir IntStream için mapToInt () kullanırsanız, üzerinde sum () çağrısı, reduce () çağrısından daha basittir.
JB Nizet

3
Bkz. Docs.oracle.com/javase/8/docs/api/java/lang/… . Integer.sum () yönteminin iki argümanı int türündedir. Bu nedenle, yönteme bağımsız değişken olarak iletilmek için akıştaki iki Tamsayı'nın kutusu kaldırılmalıdır. Yöntem bir int döndürür, ancak reduce () işlevi bir BinaryOperator <Integer> öğesini bağımsız değişken olarak alır ve bu nedenle bir Tamsayı döndürür. Bu nedenle, toplamın sonucu Tamsayıya kutulanmalıdır.
JB Nizet

28

Azaltma yöntemini kullanabilirsiniz:

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

veya

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

9
Zaten böyle bir akümülatör var int, buInteger::sum
Alex Salauyou

1
Daha iyi olurdu, böylece uzun bir dönüyoruz Long::sumdaha Integer::sum.
Andrei Damian-Fekete

16

reduce()Tamsayıların bir listesini toplamak için kullanabilirsiniz .

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

11

Tamsayı listesi eklemek için toplama yöntemini kullanabilirsiniz.

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

6

Bu, inttype dizisini ( longdizi LongStreamiçin, doubledizi için DoubleStreamvb.) Özetlemenin en kısa yolu olacaktır . StreamYine de tüm ilkel tamsayı veya kayan nokta türleri uygulamaya sahip değildir .

IntStream.of(integers).sum();

Ne yazık ki int-dizimiz yok. Bu IntStream.of()kadar ürkütücü bir şey yapmadığımız sürece bu sorun için çalışmayacak:IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
Kaplan

Gerek yok, bu yeterli olur integers.values().stream().mapToInt( Integer::intValue ).sum().
Sachith Dickwella

3

Bu, listede nesneler olanlara yardımcı olabilir.

Bir nesne listeniz varsa ve bu nesnenin belirli alanlarını toplamak istiyorsanız aşağıdakileri kullanın.

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

Teşekkürler
sahne

1

Tamsayıların bir listesini beyan ettim.

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

Bu farklı yolları aşağıda kullanmayı deneyebilirsiniz.

kullanma mapToInt

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

kullanma summarizingInt

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

kullanma 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

Özelliklerin çoğu kapsanmaktadır. Ancak, Tamsayı, Uzun dışında (özel akış desteğinin zaten mevcut olduğu) diğer veri türlerinin toplanmasını bulmak için bir gereksinim olabilir. Örneğin, BigInteger ile stram Böyle bir tür için azaltma işlemini aşağıdaki gibi kullanabiliriz:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.