1. Daftar metode Streamkelas

Kelas Streamdibuat untuk memudahkan pembuatan rantai aliran data. Untuk mencapai ini, Stream<T>kelas memiliki metode yang mengembalikan Streamobjek baru.

Masing-masing aliran data ini melakukan satu tindakan sederhana, tetapi jika Anda menggabungkannya menjadi rantai dan menambahkan fungsi lambda yang menarik , Anda memiliki mekanisme yang kuat untuk menghasilkan keluaran yang Anda inginkan. Segera Anda akan melihat sendiri.

Berikut adalah metode kelas Stream(hanya yang paling dasar):

Metode Keterangan
Stream<T> of()
Membuat aliran dari sekumpulan objek
Stream<T> generate()
Menghasilkan aliran sesuai dengan aturan yang ditentukan
Stream<T> concat()
Menggabungkan dua aliran
Stream<T> filter()
Memfilter data, hanya meneruskan data yang cocok dengan aturan yang ditentukan
Stream<T> distinct()
Menghapus duplikat. Tidak meneruskan data yang sudah ditemui
Stream<T> sorted()
Mengurutkan data
Stream<T> peek()
Melakukan tindakan pada setiap elemen dalam aliran
Stream<T> limit(n)
Mengembalikan aliran yang terpotong sehingga tidak lebih dari batas yang ditentukan
Stream<T> skip(n)
Melewati n elemen pertama
Stream<R> map()
Mengonversi data dari satu jenis ke jenis lainnya
Stream<R> flatMap()
Mengonversi data dari satu jenis ke jenis lainnya
boolean anyMatch()
Memeriksa apakah setidaknya ada satu elemen dalam aliran yang cocok dengan aturan yang ditentukan
boolean allMatch()
Memeriksa apakah semua elemen dalam aliran cocok dengan aturan yang ditentukan
boolean noneMatch()
Memeriksa apakah tidak ada elemen dalam aliran yang cocok dengan aturan yang ditentukan
Optional<T> findFirst()
Mengembalikan elemen pertama yang ditemukan cocok dengan aturan
Optional<T> findAny()
Mengembalikan elemen apa pun dalam aliran yang cocok dengan aturan
Optional<T> min()
Mencari elemen minimum dalam aliran data
Optional<T> max()
Mengembalikan elemen maksimum dalam aliran data
long count()
Mengembalikan jumlah elemen dalam aliran data
R collect()
Membaca semua data dari aliran dan mengembalikannya sebagai koleksi

2. Operasi perantara dan terminal oleh Streamkelas

Seperti yang Anda lihat, tidak semua metode dalam tabel di atas mengembalikan a Stream. Ini terkait dengan fakta bahwa metode kelas Streamdapat dibagi menjadi metode perantara (juga dikenal sebagai non-terminal ) dan metode terminal .

Metode menengah

Metode perantara mengembalikan objek yang mengimplementasikan antarmuka Stream, dan mereka dapat dirangkai bersama.

Metode terminal

Metode terminal mengembalikan nilai selain a Stream.

Saluran pemanggilan metode

Dengan demikian, Anda dapat membangun aliran pipa yang terdiri dari sejumlah metode perantara dan panggilan metode terminal tunggal di bagian akhir. Pendekatan ini memungkinkan Anda menerapkan logika yang agak rumit, sekaligus meningkatkan keterbacaan kode.

Saluran pemanggilan metode

Data di dalam aliran data tidak berubah sama sekali. Rantai metode perantara adalah cara apik (deklaratif) untuk menentukan pipa pemrosesan data yang akan dieksekusi setelah metode terminal dipanggil.

Dengan kata lain, jika metode terminal tidak dipanggil, maka data dalam aliran data tidak diproses dengan cara apa pun. Hanya setelah metode terminal dipanggil, data mulai diproses sesuai dengan aturan yang ditentukan dalam aliran pipa.

stream()
  .intemediateOperation1()
  .intemediateOperation2()
  ...
  .intemediateOperationN()
  .terminalOperation();
Penampilan umum pipa

Perbandingan metode perantara dan terminal:

intermediat terminal
Jenis pengembalian Stream tidak aStream
Dapat dikombinasikan dengan beberapa metode dari jenis yang sama untuk membentuk saluran pipa Ya TIDAK
Jumlah metode dalam satu pipeline setiap tidak lebih dari satu
Menghasilkan hasil akhir TIDAK Ya
Mulai memproses data dalam aliran TIDAK Ya

Mari kita lihat sebuah contoh.

Misalkan kita memiliki klub pecinta binatang. Besok klub merayakan Hari Kucing Jahe. Klub memiliki pemilik hewan peliharaan, yang masing-masing memiliki daftar hewan peliharaan. Mereka tidak terbatas pada kucing.

Tugas: Anda perlu mengidentifikasi semua nama semua kucing jahe untuk membuat kartu ucapan yang dipersonalisasi untuk mereka untuk "liburan profesional" besok. Kartu ucapan harus diurutkan berdasarkan usia kucing, dari yang tertua hingga yang termuda.

Pertama, kami menyediakan beberapa kelas untuk membantu menyelesaikan tugas ini:

public enum Color {
   WHITE,
   BLACK,
   DARK_GREY,
   LIGHT_GREY,
   FOXY,
   GREEN,
   YELLOW,
   BLUE,
   MAGENTA
}
public abstract class Animal {
   private String name;
   private Color color;
   private int age;

   public Animal(String name, Color color, int age) {
      this.name = name;
      this.color = color;
      this.age = age;
   }

   public String getName() {
      return name;
   }

   public Color getColor() {
      return color;
   }

   public int getAge() {
      return age;
   }
}
public class Cat extends Animal {
   public Cat(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Dog extends Animal {
   public Dog(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Parrot extends Animal {
   public Parrot(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Pig extends Animal {
   public Pig(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Snake extends Animal {
   public Snake(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Owner {
   private String name;
   private List<Animal> pets = new ArrayList<>();

   public Owner(String name) {
      this.name = name;
   }

   public List<Animal> getPets() {
      return pets;
   }
}

Sekarang mari kita lihat kelasnya Selector, di mana pemilihan akan dilakukan sesuai dengan kriteria yang ditentukan:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Selector {
   private static List<Owner> owners;

   private static void initData() {
      final Owner owner1 = new Owner("Ronan Turner");
      owner1.getPets().addAll(List.of(
            new Cat("Baron", Color.BLACK, 3),
            new Cat("Sultan", Color.DARK_GREY, 4),
            new Dog("Elsa", Color.WHITE, 0)
      ));

      final Owner owner2 = new Owner("Scarlet Murray");
      owner2.getPets().addAll(List.of(
            new Cat("Ginger", Color.FOXY, 7),
            new Cat("Oscar", Color.FOXY, 5),
            new Parrot("Admiral", Color.BLUE, 3)
      ));

      final Owner owner3 = new Owner("Felicity Mason");
      owner3.getPets().addAll(List.of(
            new Dog("Arnold", Color.FOXY, 3),
            new Pig("Vacuum Cleaner", Color.LIGHT_GREY, 8)
      ));

      final Owner owner4 = new Owner("Mitchell Stone");
      owner4.getPets().addAll(List.of(
            new Snake("Mr. Boa", Color.DARK_GREY, 2)
      ));

      final Owner owner5 = new Owner("Jonathan Snyder");
      owner5.getPets().addAll(List.of(
            new Cat("Fisher", Color.BLACK, 16),
            new Cat("Zorro", Color.FOXY, 14),
            new Cat("Margo", Color.WHITE, 3),
            new Cat("Brawler", Color.DARK_GREY, 1)
      ));

      owners = List.of(owner1, owner2, owner3, owner4, owner5);
   }
}

Yang tersisa adalah menambahkan kode ke mainmetode. Saat ini, pertama-tama kita memanggil initData()metode yang mengisi daftar pemilik hewan peliharaan di klub. Kemudian kami memilih nama kucing jahe yang diurutkan berdasarkan usianya dalam urutan menurun.

Pertama, mari kita lihat kode yang tidak menggunakan stream untuk menyelesaikan tugas ini:

public static void main(String[] args) {
   initData();

   List<String> findNames = new ArrayList<>();
   List<Cat> findCats = new ArrayList<>();
   for (Owner owner : owners) {
      for (Animal pet : owner.getPets()) {
         if (Cat.class.equals(pet.getClass()) && Color.FOXY == pet.getColor()) {
            findCats.add((Cat) pet);
         }
      }
   }

   Collections.sort(findCats, new Comparator<Cat>() {
      public int compare(Cat o1, Cat o2) {
         return o2.getAge() - o1.getAge();
      }
   });

   for (Cat cat : findCats) {
      findNames.add(cat.getName());
   }

   findNames.forEach(System.out::println);
}

Sekarang mari kita lihat alternatifnya:

public static void main(String[] args) {
   initData();

   final List<String> findNames = owners.stream()
           .flatMap(owner -> owner.getPets().stream())
           .filter(pet -> Cat.class.equals(pet.getClass()))
           .filter(cat -> Color.FOXY == cat.getColor())
           .sorted((o1, o2) -> o2.getAge() - o1.getAge())
           .map(Animal::getName)
           .collect(Collectors.toList());

   findNames.forEach(System.out::println);
}

Seperti yang Anda lihat, kodenya jauh lebih ringkas. Selain itu, setiap baris pipa aliran adalah satu tindakan, sehingga dapat dibaca seperti kalimat dalam bahasa Inggris:

.flatMap(owner -> owner.getPets().stream())
Pindah dari a Stream<Owner>ke aStream<Pet>
.filter(pet -> Cat.class.equals(pet.getClass()))
Pertahankan hanya kucing di aliran data
.filter(cat -> Color.FOXY == cat.getColor())
Pertahankan hanya jahe kucing di aliran data
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
Urutkan berdasarkan usia dalam urutan menurun
.map(Animal::getName)
Dapatkan namanya
.collect(Collectors.toList())
Masukkan hasilnya ke dalam daftar

3. Membuat aliran

Kelas Streammemiliki tiga metode yang belum kita bahas. Tujuan dari ketiga metode ini adalah untuk membuat utas baru.

Stream<T>.of(T obj)metode

Metode ini of()membuat aliran yang terdiri dari satu elemen. Ini biasanya diperlukan ketika, katakanlah, suatu fungsi mengambil Stream<T>objek sebagai argumen, tetapi Anda hanya memiliki Tobjek. Kemudian Anda dapat dengan mudah dan sederhana menggunakan of()metode tersebut untuk mendapatkan aliran yang terdiri dari satu elemen .

Contoh:

Stream<Integer> stream = Stream.of(1);

Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)metode

Metode ini of()membuat aliran yang terdiri dari elemen yang diteruskan . Sejumlah elemen diperbolehkan. Contoh:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

Stream<T> Stream.generate(Supplier<T> obj)metode

Metode ini generate()memungkinkan Anda menetapkan aturan yang akan digunakan untuk menghasilkan elemen aliran berikutnya saat diminta. Misalnya, Anda dapat memberikan nomor acak setiap kali.

Contoh:

Stream<Double> s = Stream.generate(Math::random);

Stream<T> Stream.concat(Stream<T> a, Stream<T> b)metode

Metode ini concat()menggabungkan dua aliran yang diteruskan menjadi satu . Saat data dibaca, pertama kali dibaca dari aliran pertama, lalu dari aliran kedua. Contoh:

Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = Stream.of(10, 11, 12, 13, 14);
Stream<Integer> result = Stream.concat(stream1, stream2);

4. Memfilter data

6 metode lainnya membuat aliran data baru, memungkinkan Anda menggabungkan aliran menjadi rantai (atau saluran pipa) dengan berbagai kompleksitas.

Stream<T> filter(Predicate<T>)metode

Metode ini mengembalikan aliran data baru yang memfilter aliran data sumber sesuai dengan aturan yang diteruskan . Metode harus dipanggil pada objek yang bertipe Stream<T>.

Anda dapat menentukan aturan pemfilteran menggunakan fungsi lambda , yang kemudian akan dikonversi oleh kompiler menjadi Predicate<T>objek.

Contoh:

Aliran yang dirantai Penjelasan
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x < 3));

Pertahankan hanya angka kurang dari tiga
Stream<Integer> stream = Stream.of(1, -2, 3, -4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x > 0));

Pertahankan hanya angka yang lebih besar dari nol

Stream<T> sorted(Comparator<T>)metode

Metode ini mengembalikan aliran data baru yang mengurutkan data dalam aliran sumber . Anda memasukkan komparator , yang menetapkan aturan untuk membandingkan dua elemen aliran data.

Stream<T> distinct()metode

Metode ini mengembalikan aliran data baru yang hanya berisi elemen unik dalam aliran data sumber . Semua data duplikat dibuang. Contoh:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.distinct(); // 1, 2, 3, 4, 5

Stream<T> peek(Consumer<T>)metode

Metode ini mengembalikan aliran data baru , meskipun data di dalamnya sama dengan aliran sumber. Tetapi ketika elemen berikutnya diminta dari aliran, fungsi yang Anda berikan ke peek()metode dipanggil dengannya.

Jika Anda meneruskan fungsi System.out::printlnke peek()metode, maka semua objek akan ditampilkan saat melewati aliran.

Stream<T> limit(int n)metode

Metode ini mengembalikan aliran data baru yang hanya berisi nelemen pertama dalam aliran data sumber . Semua data lainnya dibuang. Contoh:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.limit(3); // 1, 2, 3

Stream<T> skip(int n)metode

Metode ini mengembalikan aliran data baru yang berisi semua elemen yang sama dengan aliran sumber , tetapi melewatkan (mengabaikan) elemen pertama n. Contoh:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.skip(3); // 4, 5, 2, 2, 2, 3, 4