Java 8 dan Aliran Tak Terbatas

1. Ikhtisar

Pada artikel ini, kita akan melihat API java.util.Stream dan kita akan melihat bagaimana kita dapat menggunakan konstruksi itu untuk beroperasi pada aliran data / elemen yang tak terbatas.

Kemungkinan bekerja pada urutan elemen yang tak terbatas didasarkan pada fakta bahwa aliran dibuat untuk menjadi malas.

Kemalasan ini dicapai dengan pemisahan antara dua jenis operasi yang dapat dijalankan pada aliran: operasi perantara dan terminal .

2. Operasi Menengah dan Terminal

Semua Streaming operasi dibagi menjadi perantara dan terminal operasi dan digabungkan untuk membentuk jaringan pipa aliran.

Pipa aliran terdiri dari sumber (seperti Koleksi , larik, fungsi generator, saluran I / O, atau generator urutan tak terbatas); diikuti oleh nol atau lebih operasi perantara dan operasi terminal.

2.1. Operasi Menengah

Operasi menengah tidak dijalankan unit beberapa operasi terminal dipanggil.

Mereka disusun membentuk pipeline dari eksekusi Stream . The menengah operasi dapat ditambahkan ke Streaming pipa dengan metode:

  • Saring()
  • peta()
  • flatMap ()
  • berbeda()
  • diurutkan ()
  • mengintip()
  • membatasi()
  • melewatkan()

Semua operasi Menengah malas, jadi operasi tersebut tidak akan dijalankan hingga hasil pemrosesan benar-benar dibutuhkan.

Pada dasarnya, operasi perantara mengembalikan aliran baru. Menjalankan operasi perantara sebenarnya tidak melakukan operasi apa pun, tetapi membuat aliran baru yang, ketika dilintasi, berisi elemen aliran awal yang cocok dengan predikat yang diberikan.

Dengan demikian, traversal Stream tidak dimulai sampai operasi terminal dari pipeline dijalankan.

Itu adalah properti yang sangat penting, khususnya penting untuk aliran tak terbatas - karena memungkinkan kita membuat aliran yang akan benar-benar dipanggil hanya ketika operasi Terminal dipanggil.

2.2. Operasi Terminal

Operasi terminal dapat melintasi aliran untuk menghasilkan suatu hasil atau efek samping.

Setelah operasi terminal dilakukan, pipa aliran dianggap dikonsumsi, dan tidak dapat digunakan lagi. Di hampir semua kasus, operasi terminal sangat membutuhkan, menyelesaikan traversal sumber data dan pemrosesan pipeline sebelum kembali.

Semangat operasi terminal penting terkait aliran tak hingga karena pada saat pemrosesan kita perlu berpikir dengan hati-hati jika Arus kami dibatasi dengan benar , misalnya, transformasi limit () . Operasi terminal adalah:

  • untuk setiap()
  • forEachOrdered ()
  • toArray ()
  • mengurangi()
  • mengumpulkan()
  • min ()
  • maks ()
  • menghitung()
  • anyMatch ()
  • allMatch ()
  • noneMatch ()
  • findFirst ()
  • findAny ()

Masing-masing operasi ini akan memicu eksekusi semua operasi perantara.

3. Aliran Tak Terbatas

Sekarang setelah kita memahami dua konsep ini - Operasi Menengah dan Terminal - kita dapat menulis aliran tak terbatas yang memanfaatkan kemalasan Stream.

Katakanlah kita ingin membuat aliran elemen tak terbatas dari nol yang akan bertambah dua. Kemudian kita perlu membatasi urutan itu sebelum memanggil operasi terminal.

Hal ini penting untuk menggunakan batas () metode sebelum mengeksekusi collect () metode yang adalah operasi terminal, jika tidak program kami akan berjalan tanpa batas:

// given Stream infiniteStream = Stream.iterate(0, i -> i + 2); // when List collect = infiniteStream .limit(10) .collect(Collectors.toList()); // then assertEquals(collect, Arrays.asList(0, 2, 4, 6, 8, 10, 12, 14, 16, 18));

Kami membuat aliran tak terbatas menggunakan metode iterate () . Kemudian kami memanggil transformasi limit () dan operasi terminal collect () . Kemudian dalam Daftar yang dihasilkan , kita akan memiliki 10 elemen pertama dari urutan tak terbatas karena kemalasan Arus.

4. Aliran Tak Terbatas dari Jenis Elemen Kustom

Katakanlah kita ingin membuat aliran UUID acak yang tak terbatas .

Langkah pertama untuk mencapai ini menggunakan Stream API adalah membuat Pemasok dengan nilai acak tersebut:

Supplier randomUUIDSupplier = UUID::randomUUID;

Ketika kita mendefinisikan pemasok, kita dapat membuat aliran tak terbatas menggunakan metode generate () :

Stream infiniteStreamOfRandomUUID = Stream.generate(randomUUIDSupplier);

Then we could take a couple of elements from that stream. We need to remember to use a limit() method if we want our program to finish in a finite time:

List randomInts = infiniteStreamOfRandomUUID .skip(10) .limit(10) .collect(Collectors.toList());

We use a skip() transformation to discard first 10 results and take the next 10 elements. We can create an infinite stream of any custom type elements by passing a function of a Supplier interface to a generate() method on a Stream.

6. Do-While – the Stream Way

Let's say that we have a simple do..while loop in our code:

int i = 0; while (i < 10) { System.out.println(i); i++; }

We are printing i counter ten times. We can expect that such construct can be easily written using Stream API and ideally, we would have a doWhile() method on a stream.

Unfortunately, there is no such method on a stream and when we want to achieve functionality similar to standard do-while loop we need to use a limit() method:

Stream integers = Stream .iterate(0, i -> i + 1); integers .limit(10) .forEach(System.out::println);

We achieved same functionality like an imperative while loop with less code, but call to the limit() function is not as descriptive as it would be if we had a doWhile() method on a Stream object.

5. Conclusion

Artikel ini menjelaskan bagaimana kita dapat menggunakan Stream API untuk membuat streaming tanpa batas. Ini, ketika digunakan bersama dengan transformasi seperti limit () - dapat membuat beberapa skenario lebih mudah untuk dipahami dan diterapkan.

Kode yang mendukung semua contoh ini dapat ditemukan di proyek GitHub - ini adalah proyek Maven, jadi semestinya mudah untuk mengimpor dan menjalankannya apa adanya.