Stream1. Az osztály metódusainak listája

Az Streamosztályt azért hozták létre, hogy megkönnyítsék az adatfolyamok láncainak felépítését . Ennek eléréséhez az Stream<T>osztálynak olyan metódusai vannak, amelyek új Streamobjektumokat adnak vissza.

Ezen adatfolyamok mindegyike egy egyszerű műveletet hajt végre, de ha láncokká egyesíti őket, és érdekes lambda-függvényeket ad hozzá , akkor egy hatékony mechanizmus áll rendelkezésére a kívánt kimenet létrehozásához. Hamarosan meglátod magad.

Itt vannak az osztály metódusai Stream(csak a legalapvetőbbek):

Mód Leírás
Stream<T> of()
Adatfolyamot hoz létre objektumok halmazából
Stream<T> generate()
Adatfolyamot generál a megadott szabály szerint
Stream<T> concat()
Összefűz két adatfolyamot
Stream<T> filter()
Szűri az adatokat, és csak a megadott szabálynak megfelelő adatokat továbbítja
Stream<T> distinct()
Eltávolítja a duplikációkat. Nem ad át olyan adatokat, amelyekkel már találkozott
Stream<T> sorted()
Rendezi az adatokat
Stream<T> peek()
Műveletet hajt végre az adatfolyam minden elemén
Stream<T> limit(n)
Olyan adatfolyamot ad vissza, amely csonkolva van, így nem haladja meg a megadott korlátot
Stream<T> skip(n)
Kihagyja az első n elemet
Stream<R> map()
Az adatokat egyik típusból a másikba konvertálja
Stream<R> flatMap()
Az adatokat egyik típusból a másikba konvertálja
boolean anyMatch()
Ellenőrzi, hogy van-e legalább egy olyan elem az adatfolyamban, amely megfelel a megadott szabálynak
boolean allMatch()
Ellenőrzi, hogy az adatfolyam összes eleme megfelel-e a megadott szabálynak
boolean noneMatch()
Ellenőrzi, hogy az adatfolyam egyik eleme sem felel meg a megadott szabálynak
Optional<T> findFirst()
A szabálynak megfelelő első elemet adja vissza
Optional<T> findAny()
A szabálynak megfelelő adatfolyam bármely elemét adja vissza
Optional<T> min()
Megkeresi a minimális elemet az adatfolyamban
Optional<T> max()
Az adatfolyam maximális elemét adja vissza
long count()
Az adatfolyam elemeinek számát adja vissza
R collect()
Beolvassa az adatfolyam összes adatát, és gyűjteményként adja vissza

2. Köztes és végműveletek Streamosztályonként

Mint látható, a fenti táblázatban nem minden metódus ad vissza egy Stream. Ez összefügg azzal, hogy az osztály metódusai köztes (más néven nem terminális ) és terminális metódusokra Streamoszthatók .

Köztes módszerek

A köztes metódusok egy objektumot adnak vissza, amely megvalósítja az Streaminterfészt, és összeláncolhatók.

Terminál módszerek

A terminálmetódusok a -tól eltérő értéket adnak vissza Stream.

Method call pipeline

Így létrehozhat egy adatfolyam-folyamatot, amely tetszőleges számú köztes metódusból és egyetlen terminál metódushívásból áll a végén. Ez a megközelítés meglehetősen összetett logikát tesz lehetővé, miközben növeli a kód olvashatóságát.

Method call pipeline

Az adatfolyamon belüli adatok egyáltalán nem változnak. A köztes metódusok lánca egy sima (deklaratív) módja egy adatfeldolgozási folyamat megadásának, amely a terminál metódus meghívása után kerül végrehajtásra.

Más szóval, ha a terminál metódus nincs meghívva, akkor az adatfolyamban lévő adatok semmilyen módon nem kerülnek feldolgozásra. Csak a terminál metódus meghívása után kezdődik meg az adatok feldolgozása az adatfolyam-folyamatban meghatározott szabályok szerint.

stream()
  .intemediateOperation1()
  .intemediateOperation2()
  ...
  .intemediateOperationN()
  .terminalOperation();
A csővezeték általános megjelenése

A köztes és terminális módszerek összehasonlítása:

közbülső terminál
Visszatérés típusa Stream nem aStream
Több azonos típusú metódussal kombinálható csővezeték kialakításához Igen nem
Metódusok száma egyetlen folyamatban Bármi legfeljebb egy
Produkálja a végeredményt nem Igen
Elkezdi feldolgozni az adatfolyamban lévő adatokat nem Igen

Nézzünk egy példát.

Tegyük fel, hogy van egy klubunk az állatok szerelmeseinek. Holnap a klub a gyömbérmacska napját ünnepli. A klubnak vannak kisállat-tulajdonosai, mindegyiknek van házi kedvenceinek listája. Nem korlátozódnak a macskákra.

Feladat: azonosítania kell az összes gyömbérmacska nevét, hogy személyre szabott üdvözlőlapokat készítsen számukra a holnapi "szakmai ünnepre". Az üdvözlőlapokat a macska életkora szerint kell rendezni, a legidősebbtől a legfiatalabbig.

Először is biztosítunk néhány osztályt a feladat megoldásához:

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

Most nézzük meg az Selectorosztályt, ahol a kiválasztott kritériumok szerint történik:

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

Marad a kód hozzáadása a metódushoz main. Jelenleg először azt a metódust hívjuk initData(), amely feltölti a klub állattartóinak listáját. Ezután kiválasztjuk a gyömbérmacskák nevét, életkoruk szerint, csökkenő sorrendben.

Először nézzük meg azt a kódot, amely nem használ adatfolyamokat a feladat megoldásához:

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

Most nézzünk egy alternatívát:

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

Mint látható, a kód sokkal kompaktabb. Ezenkívül az adatfolyam minden sora egyetlen művelet, így angolul mondatokként olvashatók:

.flatMap(owner -> owner.getPets().stream())
Mozgás a-ból Stream<Owner>a-baStream<Pet>
.filter(pet -> Cat.class.equals(pet.getClass()))
Csak macskákat tartson meg az adatfolyamban
.filter(cat -> Color.FOXY == cat.getColor())
Csak gyömbérmacskákat tartson meg az adatfolyamban
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
Rendezés életkor szerint csökkenő sorrendben
.map(Animal::getName)
Szerezd meg a neveket
.collect(Collectors.toList())
Írja be az eredményt egy listába

3. Streamek létrehozása

Az Streamosztálynak három metódusa van, amelyekkel még nem foglalkoztunk. E három módszer célja új szálak létrehozása.

Stream<T>.of(T obj)módszer

A of()metódus egyetlen elemből álló adatfolyamot hoz létre. Erre általában akkor van szükség, ha mondjuk egy függvény egy Stream<T>objektumot vesz fel argumentumként, de Önnek csak egy Tobjektuma van. Ezután könnyen és egyszerűen használhatja a módszert, hogy egyetlen elemből álló adatfolyamotof() kapjon .

Példa:

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

Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)módszer

A of()metódus létrehoz egy adatfolyamot , amely átadott elemekből áll . Bármilyen számú elem megengedett. Példa:

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

Stream<T> Stream.generate(Supplier<T> obj)módszer

A generate()metódus segítségével beállíthat egy szabályt, amely a folyam következő elemének generálására szolgál, amikor azt kérik. Például minden alkalommal kiadhat egy véletlen számot .

Példa:

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

Stream<T> Stream.concat(Stream<T> a, Stream<T> b)módszer

A módszer összefűzi a kétconcat() átadott adatfolyamot egy . Az adatok beolvasásakor először az első adatfolyamból, majd a másodikból kerül kiolvasásra. Példa:

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. Adatok szűrése

További 6 módszer új adatfolyamokat hoz létre, amelyek lehetővé teszik az adatfolyamok különböző összetettségű láncokba (vagy csővezetékekbe) kombinálását.

Stream<T> filter(Predicate<T>)módszer

Ez a metódus egy új adatfolyamot ad vissza, amely az átadott szabály szerint szűri a forrásadatfolyamot . A metódust olyan objektumon kell meghívni, amelynek típusa .Stream<T>

A szűrési szabályt egy lambda függvény segítségével adhatja meg , amelyet a fordító ezután objektummá konvertál Predicate<T>.

Példák:

Láncos patakok Magyarázat
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x < 3));

Csak a háromnál kisebb számokat őrizze meg
Stream<Integer> stream = Stream.of(1, -2, 3, -4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x > 0));

Csak a nullánál nagyobb számokat őrizze meg

Stream<T> sorted(Comparator<T>)módszer

Ez a metódus egy új adatfolyamot ad vissza, amely rendezi az adatokat a forrásfolyamban . Átad egy összehasonlítót , amely meghatározza az adatfolyam két elemének összehasonlításának szabályait.

Stream<T> distinct()módszer

Ez a módszer egy új adatfolyamot ad vissza, amely csak a forrásadatfolyam egyedi elemeit tartalmazza . Minden ismétlődő adatot el kell dobni. Példa:

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>)módszer

Ez a metódus egy új adatfolyamot ad vissza , bár a benne lévő adatok megegyeznek a forrásfolyamban lévő adatokkal. De amikor a következő elemet kérik a folyamtól, a metódusnak átadott függvénypeek() is meghívódik vele.

Ha átadja a függvényt System.out::printlna peek()metódusnak, akkor az összes objektum megjelenik, amikor áthalad a folyamon.

Stream<T> limit(int n)módszer

Ez a módszer egy új adatfolyamot ad vissza, amely csak a forrásadatfolyam első nelemeit tartalmazza . Az összes többi adat törlésre kerül. Példa:

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)módszer

Ez a módszer egy új adatfolyamot ad vissza , amely ugyanazokat az elemeket tartalmazza, mint a forrásfolyam , de kihagyja (figyelmen kívül hagyja) az első nelemeket. Példa:

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