1. Senarai kaedah Streamkelas

Kelas Streamtelah dicipta untuk memudahkan untuk membina rantaian aliran data. Untuk mencapai ini, Stream<T>kelas mempunyai kaedah yang mengembalikan Streamobjek baharu.

Setiap strim data ini melakukan satu tindakan mudah, tetapi jika anda menggabungkannya ke dalam rantai dan menambah fungsi lambda yang menarik , maka anda mempunyai mekanisme yang berkuasa untuk menjana output yang anda inginkan. Tidak lama lagi anda akan melihat sendiri.

Berikut ialah kaedah kelas Stream(hanya yang paling asas):

Kaedah Penerangan
Stream<T> of()
Mencipta aliran daripada satu set objek
Stream<T> generate()
Menghasilkan aliran mengikut peraturan yang ditetapkan
Stream<T> concat()
Menggabungkan dua aliran
Stream<T> filter()
Menapis data, hanya menyampaikan data yang sepadan dengan peraturan yang ditentukan
Stream<T> distinct()
Mengeluarkan pendua. Tidak menyampaikan data yang telah ditemui
Stream<T> sorted()
Mengisih data
Stream<T> peek()
Melakukan tindakan pada setiap elemen dalam strim
Stream<T> limit(n)
Mengembalikan aliran yang dipotong supaya ia tidak melebihi had yang ditentukan
Stream<T> skip(n)
Melangkau n elemen pertama
Stream<R> map()
Menukar data daripada satu jenis kepada jenis yang lain
Stream<R> flatMap()
Menukar data daripada satu jenis kepada jenis yang lain
boolean anyMatch()
Menyemak sama ada terdapat sekurang-kurangnya satu elemen dalam strim yang sepadan dengan peraturan yang ditentukan
boolean allMatch()
Menyemak sama ada semua elemen dalam strim sepadan dengan peraturan yang ditentukan
boolean noneMatch()
Menyemak sama ada tiada unsur dalam strim sepadan dengan peraturan yang ditentukan
Optional<T> findFirst()
Mengembalikan elemen pertama ditemui yang sepadan dengan peraturan
Optional<T> findAny()
Mengembalikan sebarang elemen dalam strim yang sepadan dengan peraturan
Optional<T> min()
Mencari elemen minimum dalam aliran data
Optional<T> max()
Mengembalikan elemen maksimum dalam aliran data
long count()
Mengembalikan bilangan elemen dalam aliran data
R collect()
Membaca semua data daripada strim dan mengembalikannya sebagai koleksi

2. Operasi pertengahan dan terminal oleh Streamkelas

Seperti yang anda lihat, tidak semua kaedah dalam jadual di atas mengembalikan a Stream. Ini berkaitan dengan fakta bahawa kaedah kelas Streamboleh dibahagikan kepada kaedah perantaraan (juga dikenali sebagai bukan terminal ) dan kaedah terminal .

Kaedah perantaraan

Kaedah perantaraan mengembalikan objek yang melaksanakan Streamantara muka, dan ia boleh dirantai bersama.

Kaedah terminal

Kaedah terminal mengembalikan nilai selain daripada Stream.

Saluran paip panggilan kaedah

Oleh itu, anda boleh membina saluran paip aliran yang terdiri daripada sebarang bilangan kaedah perantaraan dan panggilan kaedah terminal tunggal pada penghujungnya. Pendekatan ini membolehkan anda melaksanakan logik yang agak kompleks, sambil meningkatkan kebolehbacaan kod.

Saluran paip panggilan kaedah

Data di dalam aliran data tidak berubah sama sekali. Rangkaian kaedah perantaraan ialah cara licin (deklaratif) untuk menentukan saluran paip pemprosesan data yang akan dilaksanakan selepas kaedah terminal dipanggil.

Dalam erti kata lain, jika kaedah terminal tidak dipanggil, maka data dalam aliran data tidak diproses dalam apa jua cara. Hanya selepas kaedah terminal dipanggil, data mula diproses mengikut peraturan yang ditetapkan dalam saluran paip aliran.

stream()
  .intemediateOperation1()
  .intemediateOperation2()
  ...
  .intemediateOperationN()
  .terminalOperation();
Penampilan umum saluran paip

Perbandingan kaedah perantaraan dan terminal:

perantaraan terminal
Jenis pulangan Stream bukan aStream
Boleh digabungkan dengan pelbagai kaedah dari jenis yang sama untuk membentuk saluran paip ya tidak
Bilangan kaedah dalam satu saluran paip mana-mana tidak lebih daripada satu
Menghasilkan hasil akhir tidak ya
Mula memproses data dalam strim tidak ya

Mari kita lihat satu contoh.

Katakan kita mempunyai kelab untuk pencinta haiwan. Esok kelab meraikan Hari Kucing Halia. Kelab ini mempunyai pemilik haiwan peliharaan, yang masing-masing mempunyai senarai haiwan peliharaan. Mereka tidak terhad kepada kucing.

Tugas: anda perlu mengenal pasti semua nama semua kucing halia untuk mencipta kad ucapan yang diperibadikan untuk mereka untuk "cuti profesional" esok. Kad ucapan hendaklah diisih mengikut umur kucing, daripada yang paling tua kepada yang paling muda.

Pertama, kami menyediakan beberapa kelas untuk membantu menyelesaikan tugasan 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 Selectorkelas, di mana pemilihan akan dibuat mengikut 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);
   }
}

Apa yang tinggal ialah menambah kod pada mainkaedah. Pada masa ini, kami mula-mula memanggil initData()kaedah, yang mengisi senarai pemilik haiwan peliharaan dalam kelab. Kemudian kami memilih nama kucing halia yang disusun mengikut umur mereka dalam susunan menurun.

Mula-mula, mari kita lihat kod yang tidak menggunakan strim untuk menyelesaikan tugasan 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 alternatif:

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, kod itu jauh lebih padat. Di samping itu, setiap baris saluran paip adalah satu tindakan, jadi ia boleh dibaca seperti ayat dalam bahasa Inggeris:

.flatMap(owner -> owner.getPets().stream())
Bergerak dari a Stream<Owner>ke aStream<Pet>
.filter(pet -> Cat.class.equals(pet.getClass()))
Kekalkan hanya kucing dalam strim data
.filter(cat -> Color.FOXY == cat.getColor())
Kekalkan hanya kucing halia dalam strim data
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
Isih mengikut umur dalam tertib menurun
.map(Animal::getName)
Dapatkan nama
.collect(Collectors.toList())
Letakkan hasilnya ke dalam senarai

3. Mencipta aliran

Kelas Streammempunyai tiga kaedah yang belum kami bincangkan. Tujuan ketiga-tiga kaedah ini adalah untuk mencipta benang baharu.

Stream<T>.of(T obj)kaedah

Kaedah ini of()mencipta aliran yang terdiri daripada satu elemen. Ini biasanya diperlukan apabila, katakan, fungsi mengambil Stream<T>objek sebagai hujah, tetapi anda hanya mempunyai Tobjek. Kemudian anda boleh dengan mudah dan ringkas menggunakan of()kaedah untuk mendapatkan aliran yang terdiri daripada satu elemen .

Contoh:

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

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

Kaedah ini of()mencipta aliran yang terdiri daripada elemen yang diluluskan . Sebarang bilangan elemen dibenarkan. Contoh:

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

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

Kaedah ini generate()membolehkan anda menetapkan peraturan yang akan digunakan untuk menjana elemen strim seterusnya apabila ia diminta. Sebagai contoh, anda boleh memberikan nombor rawak setiap kali.

Contoh:

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

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

Kaedah ini concat()menggabungkan dua aliran yang diluluskan menjadi satu . Apabila data dibaca, ia dibaca pertama dari aliran pertama, dan kemudian 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. Menapis data

6 kaedah lain mencipta strim data baharu, membolehkan anda menggabungkan strim ke dalam rantaian (atau saluran paip) dengan kerumitan yang berbeza-beza.

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

Kaedah ini mengembalikan aliran data baharu yang menapis aliran data sumber mengikut peraturan yang diluluskan . Kaedah mesti dipanggil pada objek yang jenisnya ialah Stream<T>.

Anda boleh menentukan peraturan penapisan menggunakan fungsi lambda , yang kemudiannya akan ditukar oleh pengkompil kepada Predicate<T>objek.

Contoh:

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

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

Kekalkan hanya nombor yang lebih besar daripada sifar

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

Kaedah ini mengembalikan aliran data baharu yang mengisih data dalam aliran sumber . Anda lulus dalam pembanding , yang menetapkan peraturan untuk membandingkan dua elemen aliran data.

Stream<T> distinct()kaedah

Kaedah ini mengembalikan aliran data baharu yang mengandungi hanya elemen unik dalam aliran data sumber . Semua data pendua 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>)kaedah

Kaedah ini mengembalikan aliran data baharu , walaupun data di dalamnya adalah sama seperti dalam aliran sumber. Tetapi apabila elemen seterusnya diminta daripada strim, fungsi yang anda hantar ke peek()kaedah akan dipanggil dengannya.

Jika anda menghantar fungsi System.out::printlnkepada peek()kaedah, maka semua objek akan dipaparkan apabila ia akan melalui aliran.

Stream<T> limit(int n)kaedah

Kaedah ini mengembalikan aliran data baharu yang mengandungi hanya elemen pertaman dalam aliran data sumber . Semua data lain 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)kaedah

Kaedah ini mengembalikan strim data baharu yang mengandungi semua elemen yang sama seperti strim sumber , tetapi melangkau (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