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();

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();

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();

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

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

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

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

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();

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

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();

-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

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))

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.