Bagaimana cara menjumlahkan daftar bilangan bulat dengan aliran java?

365

Saya ingin menjumlahkan daftar bilangan bulat. Ini berfungsi sebagai berikut, tetapi sintaksnya terasa tidak benar. Bisakah kode dioptimalkan?

Map<String, Integer> integers;
integers.values().stream().mapToInt(i -> i).sum();
anggota
sumber
5
"tetapi sintaksinya tidak terasa benar" Apa yang membuatmu berpikir seperti itu? Ini adalah ungkapan yang biasa. Mungkin Anda ingin menggunakan mapToLonguntuk menghindari luapan, tergantung pada nilai yang bisa dimiliki peta Anda.
Alexis C.
3
@ JBNizet saya temukan i -> isangat jelas, secara pribadi. Ya, Anda perlu tahu bahwa nilainya akan secara otomatis terbuka, tapi itu benar sejak Java 5 ...
Alexis C.
4
@AlexisC. ini bisa dimengerti karena dilewatkan ke mapToInt (), dan karena saya adalah pengembang yang berpengalaman. Tapi i -> i, tanpa konteks, tampak seperti noop. Integer :: intValue lebih bertele-tele, tetapi membuat operasi unboxing eksplisit.
JB Nizet
1
@JBNizet Orang yang memanggil metode foo(int i)tidak menulis foo(myInteger.intValue());setiap kali mereka menyebutnya (atau setidaknya saya harapkan tidak !!). Saya setuju dengan Anda yang Integer::intValuelebih eksplisit tetapi saya pikir hal yang sama berlaku di sini. Orang-orang harus mempelajarinya sekali saja dan kemudian selesai :-). Ini tidak seperti jika itu suatu kebingungan ajaib.
Alexis C.
4
@ JP Nizet: nah, i -> isepertinya tidak ada op dan secara konsep, itu adalah no-op. Tentu saja, di bawah tenda Integer.intValue()dipanggil, tetapi bahkan lebih dalam di bawah tenda, metode-metode tersebut diuraikan menjadi persis no-op yang terlihat seperti dalam kode sumber. Integer::intValuememiliki poin bonus tidak membuat metode sintetik dalam kode byte tetapi bukan itu yang harus mengarahkan keputusan Anda tentang bagaimana mengatur kode sumber Anda.
Holger

Jawaban:

499

Ini akan bekerja, tetapi i -> imelakukan beberapa unboxing otomatis yang mengapa "terasa" aneh. Salah satu dari yang berikut ini akan berfungsi dan lebih baik menjelaskan apa yang dilakukan kompiler di bawah tenda dengan sintaks asli Anda:

integers.values().stream().mapToInt(i -> i.intValue()).sum();
integers.values().stream().mapToInt(Integer::intValue).sum();
Necreaux
sumber
2
Bagaimana jika kita memiliki BigInteger :)?
GOXR3PLUS
13
Salah satu opsi sederhana adalahBigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
Matius
158

Saya menyarankan 2 opsi lagi:

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

Yang kedua menggunakan Collectors.summingInt()kolektor, ada juga summingLong()kolektor yang akan Anda gunakan mapToLong.


Dan opsi ketiga: Java 8 memperkenalkan LongAdderakumulator yang sangat efektif yang dirancang untuk mempercepat peringkasan dalam aliran paralel dan lingkungan multi-utas. Di sini, inilah contoh penggunaannya:

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

Dari dokumen

Operasi reduksi Operasi reduksi (juga disebut lipatan) mengambil urutan elemen input dan menggabungkannya ke dalam hasil ringkasan tunggal dengan aplikasi berulang dari operasi kombinasi, seperti menemukan jumlah atau maksimum satu set angka, atau mengumpulkan elemen ke dalam sebuah daftar. Kelas aliran memiliki beberapa bentuk operasi pengurangan umum, yang disebut mengurangi () dan mengumpulkan (), serta beberapa bentuk pengurangan khusus seperti jumlah (), maks (), atau hitung ().

Tentu saja, operasi tersebut dapat dengan mudah diimplementasikan sebagai loop berurutan sederhana, seperti pada:

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

Namun, ada alasan bagus untuk lebih memilih operasi pengurangan daripada akumulasi mutatif seperti di atas. Tidak hanya pengurangan "lebih abstrak" - ini beroperasi pada aliran secara keseluruhan daripada elemen individual - tetapi operasi pengurangan yang dibangun dengan benar secara inheren dapat diparalelkan, selama fungsi yang digunakan untuk memproses elemen-elemen tersebut asosiatif. dan tanpa kewarganegaraan. Sebagai contoh, diberi aliran angka yang ingin kita temukan jumlahnya, kita dapat menulis:

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

atau:

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

Operasi pengurangan ini dapat berjalan dengan aman secara paralel dengan hampir tidak ada modifikasi:

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

Jadi, untuk peta Anda akan menggunakan:

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

Atau:

integers.values().stream().reduce(0, Integer::sum);
J Atkin
sumber
2
Apa yang dimiliki OP jauh lebih baik, dan juga lebih jelas. Kode ini akan melibatkan seluruh operasi unboxing dan tinju.
JB Nizet
1
@JBNizet Kecuali jika analisis pelarian menghilangkan tinju. Anda harus mencobanya untuk melihat apakah itu bisa.
Peter Lawrey
6
(x, y) -> x + y perlu menghapus kotak x dan y, menjumlahkannya, dan kemudian mengotak hasilnya. Dan mulai lagi untuk menambahkan hasilnya dengan elemen aliran berikutnya, dan lagi dan lagi.
JB Nizet
3
Integer :: sum menderita masalah yang sama. Dan jika Anda menggunakan mapToInt () untuk memiliki IntStream, memanggil jumlah () di atasnya lebih mudah daripada memanggil mengurangi ().
JB Nizet
3
Lihat docs.oracle.com/javase/8/docs/api/java/lang/… . Dua argumen Integer.sum () bertipe int. Jadi dua bilangan bulat dari aliran harus dibuka untuk dilewati sebagai argumen untuk metode ini. Metode mengembalikan int, tetapi mengurangi () mengambil BinaryOperator <Integer> sebagai argumen, yang dengan demikian mengembalikan Integer. Jadi hasil penjumlahan harus kotak ke Integer.
JB Nizet
28

Anda dapat menggunakan metode pengurangan:

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

atau

long sum = result.stream().map(e -> e.getCreditAmount()).reduce(0L, Integer::sum);
Saeed Zarinfam
sumber
9
Sudah ada akumulator seperti intitu, yaituInteger::sum
Alex Salauyou
1
Anda akan kembali lama, jadi itu akan lebih baik Long::sumdaripada Integer::sum.
Andrei Damian-Fekete
16

Anda dapat menggunakan reduce()untuk menjumlahkan daftar bilangan bulat.

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

Anda dapat menggunakan metode kumpulkan untuk menambahkan daftar bilangan bulat.

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

Ini akan menjadi cara terpendek untuk meringkas intjenis array (untuk longarray LongStream, untuk doublearray DoubleStreamdan sebagainya). Namun tidak semua tipe integer primitif atau floating point Streamdiimplementasikan.

IntStream.of(integers).sum();
Sachith Dickwella
sumber
Sayangnya kami tidak memiliki int-array. Jadi IntStream.of()tidak akan bekerja untuk masalah ini, kecuali jika kita membuat sesuatu yang menyeramkan seperti ini:IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
Kaplan
Tidak perlu, ini sudah cukup integers.values().stream().mapToInt( Integer::intValue ).sum().
Sachith Dickwella
3

Semoga ini membantu mereka yang memiliki objek dalam daftar.

Jika Anda memiliki daftar objek dan ingin menjumlahkan bidang tertentu dari objek ini gunakan di bawah ini.

List<ResultSom> somList = MyUtil.getResultSom();
BigDecimal result= somList.stream().map(ResultSom::getNetto).reduce(
                                             BigDecimal.ZERO, BigDecimal::add);
itro
sumber
Terima kasih itu membantu saya dalam salah satu scenerio saya
A_01
1

Saya telah mendeklarasikan daftar Integer.

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

Anda dapat mencoba menggunakan berbagai cara di bawah ini.

Menggunakan mapToInt

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

Menggunakan summarizingInt

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

Menggunakan reduce

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

Sebagian besar aspek tercakup. Tetapi mungkin ada persyaratan untuk menemukan agregasi tipe data lain selain dari Integer, Long (yang sudah disediakan dukungan aliran khusus). Untuk mis. Stram dengan BigInteger Untuk tipe seperti itu kita dapat menggunakan seperti mengurangi operasi

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

ManojG
sumber