1. Listahan ng mga pamamaraan ng Streamklase

Nilikha ang Streamklase upang gawing madali ang pagbuo ng mga chain ng mga stream ng data. Upang makamit ito, ang Stream<T>klase ay may mga pamamaraan na nagbabalik ng mga bagong Streambagay.

Ang bawat isa sa mga stream ng data na ito ay gumagawa ng isang simpleng aksyon, ngunit kung pagsasamahin mo ang mga ito sa mga chain at magdagdag ng mga kawili-wiling mga function ng lambda , kung gayon mayroon kang makapangyarihang mekanismo para sa pagbuo ng output na gusto mo. Sa lalong madaling panahon makikita mo para sa iyong sarili.

Narito ang mga pamamaraan ng Streamklase (ang pinakapangunahing mga pamamaraan lamang):

Paraan Paglalarawan
Stream<T> of()
Lumilikha ng isang stream mula sa isang hanay ng mga bagay
Stream<T> generate()
Bumubuo ng stream ayon sa tinukoy na panuntunan
Stream<T> concat()
Pinagsasama ang dalawang batis
Stream<T> filter()
Sinasala ang data, nagpapasa lang ng data na tumutugma sa tinukoy na panuntunan
Stream<T> distinct()
Tinatanggal ang mga duplicate. Hindi nagpapasa ng data na nakatagpo na
Stream<T> sorted()
Pinag-uuri-uri ang data
Stream<T> peek()
Nagsasagawa ng pagkilos sa bawat elemento sa stream
Stream<T> limit(n)
Nagbabalik ng stream na pinutol upang hindi na ito lumampas sa tinukoy na limitasyon
Stream<T> skip(n)
Nilaktawan ang unang n elemento
Stream<R> map()
Kino-convert ang data mula sa isang uri patungo sa isa pa
Stream<R> flatMap()
Kino-convert ang data mula sa isang uri patungo sa isa pa
boolean anyMatch()
Sinusuri kung mayroong kahit isang elemento sa stream na tumutugma sa tinukoy na panuntunan
boolean allMatch()
Sinusuri kung ang lahat ng elemento sa stream ay tumutugma sa tinukoy na panuntunan
boolean noneMatch()
Sinusuri kung wala sa mga elemento sa stream ang tumutugma sa tinukoy na panuntunan
Optional<T> findFirst()
Ibinabalik ang unang elementong nakita na tumutugma sa panuntunan
Optional<T> findAny()
Ibinabalik ang anumang elemento sa stream na tumutugma sa panuntunan
Optional<T> min()
Hinahanap ang pinakamababang elemento sa stream ng data
Optional<T> max()
Ibinabalik ang maximum na elemento sa stream ng data
long count()
Ibinabalik ang bilang ng mga elemento sa stream ng data
R collect()
Binabasa ang lahat ng data mula sa stream at ibinabalik ito bilang isang koleksyon

2. Intermediate at terminal operations ng Streamklase

Tulad ng nakikita mo, hindi lahat ng mga pamamaraan sa talahanayan sa itaas ay nagbabalik ng Stream. Ito ay nauugnay sa katotohanan na ang mga pamamaraan ng Streamklase ay maaaring nahahati sa mga intermediate (kilala rin bilang non-terminal ) na mga pamamaraan at terminal na pamamaraan.

Mga intermediate na pamamaraan

Ang mga intermediate na pamamaraan ay nagbabalik ng isang bagay na nagpapatupad ng Streaminterface, at maaari silang magkakadena.

Mga pamamaraan ng terminal

Ang mga pamamaraan ng terminal ay nagbabalik ng isang halaga maliban sa isang Stream.

Paraan ng tawag sa pipeline

Kaya, maaari kang bumuo ng isang stream pipeline na binubuo ng anumang bilang ng mga intermediate na pamamaraan at isang solong terminal method na tawag sa dulo. Hinahayaan ka ng diskarteng ito na ipatupad ang medyo kumplikadong lohika, habang pinapataas ang pagiging madaling mabasa ng code.

Paraan ng tawag sa pipeline

Ang data sa loob ng isang stream ng data ay hindi nagbabago. Ang isang kadena ng mga intermediate na pamamaraan ay isang makinis (declarative) na paraan ng pagtukoy ng pipeline sa pagpoproseso ng data na isasagawa pagkatapos tawagin ang terminal method.

Sa madaling salita, kung ang paraan ng terminal ay hindi tinatawag, kung gayon ang data sa stream ng data ay hindi naproseso sa anumang paraan. Pagkatapos lamang na tawagin ang paraan ng terminal, magsisimulang maproseso ang data ayon sa mga patakarang tinukoy sa pipeline ng stream.

stream()
  .intemediateOperation1()
  .intemediateOperation2()
  ...
  .intemediateOperationN()
  .terminalOperation();
Pangkalahatang hitsura ng isang pipeline

Paghahambing ng mga intermediate at terminal na pamamaraan:

nasa pagitan terminal
Uri ng pagbabalik Stream hindi aStream
Maaaring pagsamahin sa maraming paraan ng parehong uri upang bumuo ng pipeline oo hindi
Bilang ng mga pamamaraan sa isang pipeline anuman hindi hihigit sa isa
Gumagawa ng huling resulta hindi oo
Nagsisimulang iproseso ang data sa stream hindi oo

Tingnan natin ang isang halimbawa.

Ipagpalagay na mayroon kaming isang club para sa mga mahilig sa hayop. Bukas ipinagdiriwang ng club ang Ginger Cat Day. Ang club ay may mga may-ari ng alagang hayop, bawat isa ay may listahan ng mga alagang hayop. Hindi sila limitado sa mga pusa.

Gawain: kailangan mong tukuyin ang lahat ng pangalan ng lahat ng luya na pusa upang makagawa ng mga personalized na greeting card para sa kanila para sa "propesyonal na holiday" bukas. Ang mga greeting card ay dapat na pinagsunod-sunod ayon sa edad ng pusa, mula sa pinakamatanda hanggang sa pinakabata.

Una, nagbibigay kami ng ilang klase upang makatulong sa paglutas ng gawaing ito:

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

Ngayon tingnan natin ang Selectorklase, kung saan ang pagpili ay gagawin ayon sa tinukoy na pamantayan:

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

Ang natitira ay magdagdag ng code sa mainpamamaraan. Sa kasalukuyan, una naming tinawag ang initData()pamamaraan, na naninirahan sa listahan ng mga may-ari ng alagang hayop sa club. Pagkatapos ay pipiliin namin ang mga pangalan ng luya na pusa na pinagsunod-sunod ayon sa kanilang edad sa pababang pagkakasunud-sunod.

Una, tingnan natin ang code na hindi gumagamit ng mga stream upang malutas ang gawaing ito:

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

Ngayon tingnan natin ang isang alternatibo:

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

Tulad ng nakikita mo, ang code ay mas compact. Bilang karagdagan, ang bawat linya ng stream pipeline ay iisang aksyon, para mabasa ang mga ito tulad ng mga pangungusap sa English:

.flatMap(owner -> owner.getPets().stream())
Ilipat mula sa a Stream<Owner>tungo sa aStream<Pet>
.filter(pet -> Cat.class.equals(pet.getClass()))
Panatilihin lamang ang mga pusa sa stream ng data
.filter(cat -> Color.FOXY == cat.getColor())
Panatilihin lamang ang mga luya na pusa sa stream ng data
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
Pagbukud-bukurin ayon sa edad sa pababang pagkakasunud-sunod
.map(Animal::getName)
Kunin ang mga pangalan
.collect(Collectors.toList())
Ilagay ang resulta sa isang listahan

3. Paglikha ng mga stream

Ang Streamklase ay may tatlong pamamaraan na hindi pa namin sakop. Ang layunin ng tatlong pamamaraang ito ay lumikha ng mga bagong thread.

Stream<T>.of(T obj)paraan

Ang of()pamamaraan ay lumilikha ng isang stream na binubuo ng isang solong elemento. Karaniwang kailangan ito kapag, sabihin nating, ang isang function ay tumatagal ng isang Stream<T>bagay bilang isang argumento, ngunit mayroon ka lamang isang Tbagay. Pagkatapos ay madali at simpleng magagamit mo ang of()paraan upang makakuha ng stream na binubuo ng isang elemento .

Halimbawa:

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

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

Ang of()pamamaraan ay lumilikha ng isang stream na binubuo ng mga pumasa na elemento . Ang anumang bilang ng mga elemento ay pinapayagan. Halimbawa:

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

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

Hinahayaan ka ng generate()paraan na magtakda ng panuntunan na gagamitin para bumuo ng susunod na elemento ng stream kapag hiniling ito. Halimbawa, maaari kang magbigay ng random na numero sa bawat oras.

Halimbawa:

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

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

Pinagsasama-sama ng concat()pamamaraan ang dalawang dumaan na stream sa isa . Kapag nabasa ang data , babasahin muna ito mula sa unang stream, at pagkatapos ay mula sa pangalawa. Halimbawa:

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. Pag-filter ng data

Ang isa pang 6 na paraan ay lumikha ng mga bagong stream ng data, na nagpapahintulot sa iyong pagsamahin ang mga stream sa mga chain (o pipeline) na may iba't ibang kumplikado.

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

Ang pamamaraang ito ay nagbabalik ng bagong stream ng data na nagpi-filter sa source data stream ayon sa ipinasang panuntunan . Ang pamamaraan ay dapat na tinatawag sa isang bagay na ang uri ay Stream<T>.

Maaari mong tukuyin ang panuntunan sa pag-filter gamit ang isang lambda function , na pagkatapos ay iko-convert ng compiler sa isang Predicate<T>object.

Mga halimbawa:

Mga nakakadena na sapa Paliwanag
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x < 3));

Panatilihin lamang ang mga numerong mas mababa sa tatlo
Stream<Integer> stream = Stream.of(1, -2, 3, -4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x > 0));

Panatilihin lamang ang mga numerong mas malaki sa zero

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

Ang pamamaraang ito ay nagbabalik ng bagong stream ng data na nag-uuri ng data sa source stream . Pumasa ka sa isang comparator , na nagtatakda ng mga panuntunan para sa paghahambing ng dalawang elemento ng stream ng data.

Stream<T> distinct()paraan

Ang pamamaraang ito ay nagbabalik ng bagong stream ng data na naglalaman lamang ng mga natatanging elemento sa source data stream . Itatapon ang lahat ng duplicate na data. Halimbawa:

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

Ang pamamaraang ito ay nagbabalik ng bagong stream ng data , kahit na ang data sa loob nito ay kapareho ng sa source stream. Ngunit kapag ang susunod na elemento ay hiniling mula sa stream, ang function na ipinasa mo sa peek()pamamaraan ay tatawagin kasama nito.

Kung ipapasa mo ang function System.out::printlnsa peek()pamamaraan, ang lahat ng mga bagay ay ipapakita kapag sila ay dumaan sa stream.

Stream<T> limit(int n)paraan

Ang pamamaraang ito ay nagbabalik ng bagong stream ng data na naglalaman lamang ng mga unang nelemento sa source data stream . Ang lahat ng iba pang data ay itinapon. Halimbawa:

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

Ang pamamaraang ito ay nagbabalik ng bagong stream ng data na naglalaman ng lahat ng parehong elemento gaya ng source stream , ngunit nilalaktawan (binalewala) ang mga unang nelemento. Halimbawa:

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