JavaRush /Blog Java /Random-MS /Coffee break #177. Panduan Terperinci untuk Java Stream d...

Coffee break #177. Panduan Terperinci untuk Java Stream di Java 8

Diterbitkan dalam kumpulan
Sumber: Hackernoon Siaran ini menyediakan tutorial terperinci tentang bekerja dengan Java Stream bersama-sama dengan contoh dan penjelasan kod. Coffee break #177.  Panduan Terperinci untuk Java Stream dalam Java 8 - 1

Pengenalan kepada Benang Java dalam Java 8

Java Streams, yang diperkenalkan sebagai sebahagian daripada Java 8, digunakan untuk bekerja dengan koleksi data. Mereka bukan struktur data itu sendiri, tetapi boleh digunakan untuk memasukkan maklumat daripada struktur data lain dengan membuat pesanan dan saluran paip untuk menghasilkan keputusan akhir. Nota: Adalah penting untuk tidak mengelirukan Stream dan Thread, kerana dalam bahasa Rusia kedua-dua istilah sering dirujuk dalam terjemahan yang sama "aliran". Strim menandakan objek untuk menjalankan operasi (paling kerap memindahkan atau menyimpan data), manakala Thread (terjemahan literal - benang) menandakan objek yang membenarkan kod program tertentu dilaksanakan selari dengan cawangan kod lain. Oleh kerana Stream bukan struktur data yang berasingan, ia tidak pernah mengubah sumber data. Aliran Java mempunyai ciri-ciri berikut:
  1. Java Stream boleh digunakan menggunakan pakej "java.util.stream". Ia boleh diimport ke dalam skrip menggunakan kod:

    import java.util.stream.* ;

    Menggunakan kod ini, kami juga boleh melaksanakan beberapa fungsi terbina dalam dengan mudah dalam Java Stream.

  2. Java Stream boleh menerima input daripada pengumpulan data seperti koleksi dan tatasusunan dalam Java.

  3. Java Stream tidak memerlukan perubahan struktur data input.

  4. Java Stream tidak mengubah sumber. Sebaliknya, ia menjana output menggunakan kaedah saluran paip yang sesuai.

  5. Java Streams tertakluk kepada operasi perantaraan dan terminal, yang akan kita bincangkan dalam bahagian berikut.

  6. Dalam Java Stream, operasi perantaraan disalurkan dan berlaku dalam format penilaian yang malas. Mereka berakhir dengan fungsi terminal. Ini membentuk format asas untuk menggunakan Java Stream.

Dalam bahagian seterusnya, kita akan melihat pelbagai kaedah yang digunakan dalam Java 8 untuk mencipta Java Stream apabila diperlukan.

Mencipta Java Stream di Java 8

Benang Java boleh dibuat dalam beberapa cara:

1. Mencipta strim kosong menggunakan kaedah Stream.empty().

Anda boleh membuat strim kosong untuk digunakan kemudian dalam kod anda. Jika anda menggunakan kaedah Stream.empty() , strim kosong akan dijana, tidak mengandungi nilai. Strim kosong ini boleh berguna jika kita ingin melangkau pengecualian penuding nol semasa masa jalan. Untuk melakukan ini, anda boleh menggunakan arahan berikut:
Stream<String> str = Stream.empty();
Pernyataan di atas akan menghasilkan aliran kosong bernama str tanpa sebarang elemen di dalamnya. Untuk mengesahkan ini, cuma semak nombor atau saiz strim menggunakan istilah str.count() . Sebagai contoh,
System.out.println(str.count());
Akibatnya, kita mendapat 0 pada output .

2. Buat strim menggunakan kaedah Stream.builder() dengan contoh Stream.Builder

Kami juga boleh menggunakan Stream Builder untuk membuat strim menggunakan corak reka bentuk pembina. Ia direka untuk pembinaan objek langkah demi langkah. Mari lihat cara kita boleh membuat instantiate strim menggunakan Stream Builder .
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
Menggunakan kod ini, anda boleh mencipta strim bernama numStream yang mengandungi elemen int . Segala-galanya dilakukan dengan cepat terima kasih kepada contoh Stream.Builder yang dipanggil numBuilder yang dicipta dahulu.

3. Cipta aliran dengan nilai yang ditentukan menggunakan kaedah Stream.of().

Cara lain untuk mencipta strim melibatkan penggunaan kaedah Stream.of() . Ini ialah cara mudah untuk mencipta aliran dengan nilai yang diberikan. Ia mengisytiharkan dan juga memulakan benang. Contoh menggunakan kaedah Stream.of() untuk mencipta aliran:
Stream<Integer> numStream = Stream.of(1, 2, 3);
Kod ini akan mencipta strim yang mengandungi elemen int , seperti yang kami lakukan dalam kaedah sebelumnya menggunakan Stream.Builder . Di sini kami telah mencipta strim secara langsung menggunakan Stream.of() dengan nilai pratakrif [1, 2 dan 3] .

4. Cipta aliran daripada tatasusunan sedia ada menggunakan kaedah Arrays.stream().

Satu lagi kaedah biasa untuk mencipta benang melibatkan penggunaan tatasusunan dalam Java. Strim di sini dicipta daripada tatasusunan sedia ada menggunakan kaedah Arrays.stream() . Semua elemen tatasusunan ditukar kepada elemen strim. Berikut ialah contoh yang baik:
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
Kod ini akan menghasilkan numStream yang mengandungi kandungan tatasusunan yang dipanggil arr, iaitu tatasusunan integer.

5. Menggabungkan dua aliran sedia ada menggunakan kaedah Stream.concat().

Kaedah lain yang boleh digunakan untuk mencipta aliran ialah kaedah Stream.concat() . Ia digunakan untuk menggabungkan dua utas untuk mencipta satu utas. Kedua-dua aliran digabungkan mengikut tertib. Ini bermakna benang pertama didahulukan, diikuti oleh benang kedua, dan seterusnya. Contoh penggabungan sedemikian kelihatan seperti ini:
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
Pernyataan di atas akan mencipta strim akhir bernama combinedStream yang mengandungi unsur-unsur aliran pertama numStream1 dan aliran kedua numStream2 satu demi satu .

Jenis operasi dengan Java Stream

Seperti yang telah disebutkan, anda boleh melakukan dua jenis operasi dengan Java Stream dalam Java 8: perantaraan dan terminal. Mari kita lihat setiap daripada mereka dengan lebih terperinci.

Operasi perantaraan

Operasi perantaraan menjana aliran keluaran dan dilaksanakan hanya apabila menghadapi operasi terminal. Ini bermakna bahawa operasi perantaraan dilaksanakan secara malas, saluran paip, dan hanya boleh diselesaikan dengan operasi terminal. Anda akan belajar tentang penilaian malas dan saluran paip sedikit kemudian. Contoh operasi perantaraan ialah kaedah berikut: filter() , map() , different() , peek() , sorted() dan beberapa yang lain.

Operasi Terminal

Operasi terminal melengkapkan pelaksanaan operasi perantaraan dan juga mengembalikan hasil akhir aliran keluaran. Oleh kerana operasi terminal menandakan tamatnya pelaksanaan malas dan saluran paip, benang ini tidak boleh digunakan semula selepas ia telah menjalani operasi terminal. Contoh operasi terminal ialah kaedah berikut: forEach() , collect() , count() , reduce() dan sebagainya.

Contoh operasi dengan Java Stream

Operasi perantaraan

Berikut ialah beberapa contoh beberapa operasi perantaraan yang boleh digunakan pada Java Stream:

penapis()

Kaedah ini digunakan untuk menapis elemen daripada aliran yang sepadan dengan predikat tertentu dalam Java. Item yang ditapis ini kemudiannya membentuk strim baharu. Mari kita lihat contoh untuk lebih memahami dengan lebih baik.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
Kesimpulan:
[98]
Penjelasan: Dalam contoh ini, anda boleh melihat bahawa elemen genap (boleh dibahagikan dengan 2) ditapis menggunakan kaedah filter() dan disimpan dalam senarai integer numStream , yang kandungannya akan dicetak kemudian. Memandangkan 98 ialah satu-satunya integer genap dalam aliran, ia dicetak dalam output.

peta()

Kaedah ini digunakan untuk mencipta aliran baharu dengan melaksanakan fungsi dipetakan pada elemen aliran input asal. Mungkin aliran baharu mempunyai jenis data yang berbeza. Contohnya kelihatan seperti ini:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
Kesimpulan:
[86, 130, 2, 196, 126]
Penjelasan: Di sini kita melihat bahawa kaedah map() digunakan untuk menggandakan setiap elemen aliran numStream . Seperti yang anda boleh lihat daripada output, setiap elemen dalam aliran telah berjaya digandakan.

berbeza()

Kaedah ini digunakan untuk mendapatkan semula elemen individu sahaja dalam strim dengan menapis pendua. Contoh yang sama kelihatan seperti ini:
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
Kesimpulan:
[43, 65, 1, 98, 63]
Penjelasan: Dalam kes ini, kaedah different() digunakan untuk numStream . Ia mendapatkan semula semua elemen individu dalam numList dengan mengalih keluar pendua daripada strim. Seperti yang anda lihat daripada output, tiada pendua, tidak seperti aliran input, yang pada mulanya mempunyai dua pendua (63 dan 1).

intip()

Kaedah ini digunakan untuk menjejaki perubahan perantaraan sebelum melaksanakan operasi terminal. Ini bermakna peek() boleh digunakan untuk melakukan operasi pada setiap elemen strim untuk mencipta strim di mana operasi perantaraan selanjutnya boleh dilakukan.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
Kesimpulan:
Dipetakan: 430 Dipetakan: 650 Dipetakan: 10 Dipetakan: 980 Dipetakan: 630 [430, 650, 10, 980, 630]
Penjelasan: Di sini kaedah peek() digunakan untuk menjana hasil perantaraan kerana kaedah map() digunakan pada elemen aliran. Di sini kita dapat melihat bahawa walaupun sebelum menggunakan operasi terminal collect() untuk mencetak kandungan akhir senarai dalam pernyataan cetakan , keputusan untuk setiap pemetaan elemen aliran dicetak secara berurutan terlebih dahulu.

disusun()

Kaedah sorted() digunakan untuk mengisih unsur-unsur aliran. Secara lalai, ia menyusun elemen dalam tertib menaik. Anda juga boleh menentukan susunan isihan tertentu sebagai parameter. Contoh pelaksanaan kaedah ini kelihatan seperti ini:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
Kesimpulan:
1 43 ​​63 65 98
Penjelasan: Di sini, kaedah sorted() digunakan untuk mengisih elemen aliran dalam tertib menaik secara lalai (kerana tiada susunan khusus ditentukan). Anda boleh melihat bahawa item yang dicetak dalam output disusun dalam tertib menaik.

Operasi Terminal

Berikut ialah beberapa contoh beberapa operasi terminal yang boleh digunakan pada aliran Java:

untuk setiap()

Kaedah forEach() digunakan untuk mengulangi semua elemen aliran dan melaksanakan fungsi pada setiap elemen satu demi satu. Ini bertindak sebagai alternatif kepada pernyataan gelung seperti untuk , sementara dan lain-lain. Contoh:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
Kesimpulan:
43 65 1 98 63
Penjelasan: Di sini kaedah forEach() digunakan untuk mencetak setiap elemen aliran satu demi satu.

kira()

Kaedah count() digunakan untuk mendapatkan semula jumlah elemen yang terdapat dalam strim. Ia serupa dengan kaedah size() , yang sering digunakan untuk menentukan jumlah bilangan elemen dalam koleksi. Contoh menggunakan kaedah count() dengan Java Stream adalah seperti berikut:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
Kesimpulan:
5
Penjelasan: Memandangkan numStream mengandungi 5 elemen integer, menggunakan kaedah count() padanya akan mengeluarkan 5.

kumpulkan()

Kaedah collect() digunakan untuk melakukan pengurangan boleh ubah bagi elemen aliran. Ia boleh digunakan untuk mengalih keluar kandungan daripada strim selepas pemprosesan selesai. Ia menggunakan kelas Pemungut untuk melakukan pengurangan .
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
Kesimpulan:
[43, 65, 1, 63]
Penjelasan: Dalam contoh ini, semua elemen ganjil dalam strim ditapis dan dikumpulkan/dikurangkan ke dalam senarai bernama ganjil . Pada akhirnya, senarai yang ganjil dicetak.

min() dan max()

Kaedah min() , seperti namanya, boleh digunakan pada aliran untuk mencari elemen minimum di dalamnya. Begitu juga, kaedah max() boleh digunakan untuk mencari elemen maksimum dalam aliran. Mari cuba fahami bagaimana ia boleh digunakan dengan contoh:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
Kesimpulan:
Elemen terkecil: 1 Elemen terbesar: 98
Penjelasan: Dalam contoh ini, kami mencetak elemen terkecil dalam numStream menggunakan kaedah min () dan elemen terbesar menggunakan kaedah max() . Ambil perhatian bahawa di sini, sebelum menggunakan kaedah max() , kami telah menambah elemen pada numStream sekali lagi . Ini kerana min() ialah operasi terminal dan memusnahkan kandungan aliran asal, hanya mengembalikan hasil akhir (yang dalam kes ini ialah integer "terkecil").

findAny() dan findFirst()

findAny() mengembalikan mana-mana elemen aliran sebagai Optional . Jika strim kosong, ia juga akan mengembalikan nilai Pilihan , yang akan kosong. findFirst() mengembalikan elemen pertama aliran sebagai Pilihan . Seperti kaedah findAny() , kaedah findFirst() juga mengembalikan parameter Pilihan kosong jika aliran yang sepadan kosong. Mari kita lihat contoh berikut berdasarkan kaedah ini:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
Kesimpulan:
Pilihan[43] Pilihan.kosong
Penjelasan: Di sini, dalam kes pertama, kaedah findFirst() mengembalikan elemen pertama aliran sebagai Pilihan . Kemudian, apabila utas ditetapkan semula sebagai utas kosong, kaedah findAny() mengembalikan Optional kosong .

allMatch() , anyMatch() dan noneMatch()

Kaedah allMatch() digunakan untuk menyemak sama ada semua elemen dalam strim sepadan dengan predikat tertentu dan mengembalikan nilai boolean benar jika ia berlaku, sebaliknya mengembalikan false . Jika strim kosong, ia kembali benar . Kaedah anyMatch() digunakan untuk menyemak sama ada mana-mana elemen dalam aliran sepadan dengan predikat tertentu. Ia mengembalikan benar jika ya, palsu sebaliknya. Jika strim kosong, ia mengembalikan false . Kaedah noneMatch() mengembalikan benar jika tiada unsur dalam strim sepadan dengan predikat, dan palsu sebaliknya. Contoh untuk menggambarkan ini kelihatan seperti ini:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
Kesimpulan:
palsu benar palsu
Penjelasan: Untuk aliran numStream yang mengandungi 1 sebagai elemen, kaedah allMatch() mengembalikan palsu kerana semua elemen bukan 1, tetapi hanya satu daripadanya. Kaedah anyMatch() mengembalikan benar kerana sekurang-kurangnya satu daripada elemen ialah 1. Kaedah noneMatch() mengembalikan palsu kerana 1 sebenarnya wujud sebagai elemen dalam aliran ini.

Penilaian Malas dalam Java Stream

Penilaian malas membawa kepada pengoptimuman apabila bekerja dengan Java Streams dalam Java 8. Ini terutamanya melibatkan penangguhan operasi perantaraan sehingga operasi terminal ditemui. Penilaian malas bertanggungjawab untuk mencegah pembaziran sumber yang tidak perlu pada pengiraan sehingga hasilnya benar-benar diperlukan. Aliran keluaran yang terhasil daripada operasi perantaraan dijana hanya selepas operasi terminal selesai. Penilaian malas berfungsi dengan semua operasi perantaraan dalam aliran Java. Penggunaan penilaian malas yang sangat berguna berlaku apabila bekerja dengan aliran tak terhingga. Dengan cara ini, banyak pemprosesan yang tidak perlu dihalang.

Saluran paip dalam Java Stream

Saluran paip dalam Java Stream terdiri daripada aliran input, sifar atau lebih operasi perantaraan berbaris satu demi satu, dan akhirnya operasi terminal. Operasi perantaraan dalam Java Streams dilakukan dengan malas. Ini menjadikan operasi perantaraan saluran paip tidak dapat dielakkan. Dengan saluran paip, yang pada asasnya merupakan operasi perantaraan yang digabungkan dalam susunan, pelaksanaan malas menjadi mungkin. Talian paip membantu menjejaki operasi perantaraan yang perlu dilakukan selepas operasi terminal akhirnya ditemui.

Kesimpulan

Mari kita ringkaskan apa yang telah kita pelajari hari ini. Dalam artikel ini:
  1. Kami melihat dengan pantas apa itu Java Stream.
  2. Kami kemudian mempelajari banyak teknik yang berbeza untuk mencipta benang Java dalam Java 8.
  3. Kami mempelajari dua jenis operasi utama (operasi perantaraan dan operasi terminal) yang boleh dilakukan pada aliran Java.
  4. Kami kemudian melihat secara terperinci beberapa contoh operasi perantaraan dan terminal.
  5. Kami akhirnya mempelajari lebih lanjut tentang penilaian malas dan akhirnya belajar tentang saluran paip dalam benang Java.
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION