Stream
1. Az osztály metódusainak listája
Az Stream
osztá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 Stream
objektumokat 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 |
---|---|
|
Adatfolyamot hoz létre objektumok halmazából |
|
Adatfolyamot generál a megadott szabály szerint |
|
Összefűz két adatfolyamot |
|
Szűri az adatokat, és csak a megadott szabálynak megfelelő adatokat továbbítja |
|
Eltávolítja a duplikációkat. Nem ad át olyan adatokat, amelyekkel már találkozott |
|
Rendezi az adatokat |
|
Műveletet hajt végre az adatfolyam minden elemén |
|
Olyan adatfolyamot ad vissza, amely csonkolva van, így nem haladja meg a megadott korlátot |
|
Kihagyja az első n elemet |
|
Az adatokat egyik típusból a másikba konvertálja |
|
Az adatokat egyik típusból a másikba konvertálja |
|
Ellenőrzi, hogy van-e legalább egy olyan elem az adatfolyamban, amely megfelel a megadott szabálynak |
|
Ellenőrzi, hogy az adatfolyam összes eleme megfelel-e a megadott szabálynak |
|
Ellenőrzi, hogy az adatfolyam egyik eleme sem felel meg a megadott szabálynak |
|
A szabálynak megfelelő első elemet adja vissza |
|
A szabálynak megfelelő adatfolyam bármely elemét adja vissza |
|
Megkeresi a minimális elemet az adatfolyamban |
|
Az adatfolyam maximális elemét adja vissza |
|
Az adatfolyam elemeinek számát adja vissza |
|
Beolvassa az adatfolyam összes adatát, és gyűjteményként adja vissza |
2. Köztes és végműveletek Stream
osztá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 Stream
oszthatók .
Köztes módszerek
A köztes metódusok egy objektumot adnak vissza, amely megvalósítja az Stream
interfé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.
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 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 Selector
osztá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:
|
Mozgás a-ból Stream<Owner> a-baStream<Pet> |
|
Csak macskákat tartson meg az adatfolyamban |
|
Csak gyömbérmacskákat tartson meg az adatfolyamban |
|
Rendezés életkor szerint csökkenő sorrendben |
|
Szerezd meg a neveket |
|
Írja be az eredményt egy listába |
3. Streamek létrehozása
Az Stream
osztá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 T
objektuma 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 |
---|---|
|
Csak a háromnál kisebb számokat őrizze meg |
|
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::println
a 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ő n
elemeit 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ő n
elemeket. 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
GO TO FULL VERSION