1. Lista metodelor Stream
clasei
Clasa Stream
a fost creată pentru a facilita construirea de lanțuri de fluxuri de date. Pentru a realiza acest lucru, Stream<T>
clasa are metode care returnează noi Stream
obiecte.
Fiecare dintre aceste fluxuri de date efectuează o acțiune simplă, dar dacă le combinați în lanțuri și adăugați funcții lambda interesante , atunci aveți un mecanism puternic pentru a genera rezultatul dorit. În curând vei vedea singur.
Iată metodele clasei Stream
(doar cele mai elementare):
Metode | Descriere |
---|---|
|
Creează un flux dintr-un set de obiecte |
|
Generează un flux conform regulii specificate |
|
Concatenează două fluxuri |
|
Filtrează datele, trimițând numai datele care se potrivesc cu regula specificată |
|
Îndepărtează duplicatele. Nu transmite date care au fost deja întâlnite |
|
Sortează datele |
|
Efectuează o acțiune asupra fiecărui element din flux |
|
Returnează un flux care este trunchiat astfel încât să nu depășească limita specificată |
|
Omite primele n elemente |
|
Convertește datele de la un tip la altul |
|
Convertește datele de la un tip la altul |
|
Verifică dacă există cel puțin un element în flux care se potrivește cu regula specificată |
|
Verifică dacă toate elementele din flux se potrivesc cu regula specificată |
|
Verifică dacă niciunul dintre elementele din flux nu se potrivește cu regula specificată |
|
Returnează primul element găsit care se potrivește cu regula |
|
Returnează orice element din flux care se potrivește cu regula |
|
Caută elementul minim din fluxul de date |
|
Returnează elementul maxim din fluxul de date |
|
Returnează numărul de elemente din fluxul de date |
|
Citește toate datele din flux și le returnează ca o colecție |
2. Operații intermediare și terminale de către Stream
clasă
După cum puteți vedea, nu toate metodele din tabelul de mai sus returnează un Stream
. Acest lucru este legat de faptul că metodele clasei Stream
pot fi împărțite în metode intermediare (cunoscute și ca non-terminale ) și metode terminale .
Metode intermediare
Metodele intermediare returnează un obiect care implementează Stream
interfața și pot fi înlănțuite împreună.
Metode terminale
Metodele terminale returnează o altă valoare decât un Stream
.
Conducta de apeluri de metodă
Astfel, puteți construi o conductă de flux constând din orice număr de metode intermediare și un singur apel de metodă terminal la sfârșit. Această abordare vă permite să implementați o logică destul de complexă, sporind în același timp lizibilitatea codului.

Datele din interiorul unui flux de date nu se modifică deloc. Un lanț de metode intermediare este o modalitate inteligentă (declarativă) de a specifica o conductă de procesare a datelor care va fi executată după apelarea metodei terminale.
Cu alte cuvinte, dacă metoda terminalului nu este apelată, atunci datele din fluxul de date nu sunt procesate în niciun fel. Abia după apelarea metodei terminale, datele încep să fie procesate conform regulilor specificate în conducta de flux.
stream()
.intemediateOperation1()
.intemediateOperation2()
...
.intemediateOperationN()
.terminalOperation();
Comparația metodelor intermediare și terminale:
intermediar | Terminal | |
---|---|---|
Tip de returnare | Stream |
nu aStream |
Poate fi combinat cu mai multe metode de același tip pentru a forma o conductă | da | Nu |
Numărul de metode dintr-o singură conductă | orice | nu mai mult de unul |
Produce rezultatul final | Nu | da |
Începe procesarea datelor din flux | Nu | da |
Să ne uităm la un exemplu.
Să presupunem că avem un club pentru iubitorii de animale. Mâine clubul sărbătorește Ziua Pisicii Ghimbir. Clubul are proprietari de animale de companie, fiecare având o listă de animale de companie. Nu se limitează la pisici.
Sarcină: trebuie să identifici toate numele tuturor pisicilor ghimbir pentru a crea felicitări personalizate pentru „sărbătoarea profesională” de mâine. Felicitarile trebuie sortate in functie de varsta pisicii, de la cea mai mare la cea mai mica.
În primul rând, oferim câteva clase pentru a ajuta la rezolvarea acestei sarcini:
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;
}
}
Acum să ne uităm la Selector
clasa, unde selecția se va face conform criteriilor specificate:
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);
}
}
Ceea ce rămâne este să adăugați cod la main
metodă. În prezent, numim mai întâi initData()
metoda, care populează lista proprietarilor de animale de companie din club. Apoi selectăm numele pisicilor ghimbir sortate după vârsta lor în ordine descrescătoare.
Mai întâi, să vedem codul care nu folosește fluxuri pentru a rezolva această sarcină:
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);
}
Acum să ne uităm la o alternativă:
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);
}
După cum puteți vedea, codul este mult mai compact. În plus, fiecare linie a conductei de flux este o singură acțiune, astfel încât acestea pot fi citite ca propoziții în engleză:
|
Treceți de la a Stream<Owner> la aStream<Pet> |
|
Păstrați numai pisicile în fluxul de date |
|
Păstrați numai pisici de ghimbir în fluxul de date |
|
Sortați după vârstă în ordine descrescătoare |
|
Obțineți numele |
|
Puneți rezultatul într-o listă |
3. Crearea fluxurilor
Clasa Stream
are trei metode pe care nu le-am acoperit încă. Scopul acestor trei metode este de a crea fire noi.
Stream<T>.of(T obj)
metodă
Metoda of()
creează un flux care constă dintr-un singur element. Acest lucru este de obicei necesar atunci când, de exemplu, o funcție ia un Stream<T>
obiect ca argument, dar aveți doar un T
obiect. Apoi puteți utiliza cu ușurință și simplu of()
metoda pentru a obține un flux care constă dintr-un singur element .
Exemplu:
Stream<Integer> stream = Stream.of(1);
Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)
metodă
Metoda of()
creează un flux care constă din elemente trecute . Este permis orice număr de elemente. Exemplu:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<T> Stream.generate(Supplier<T> obj)
metodă
Metoda generate()
vă permite să setați o regulă care va fi folosită pentru a genera următorul element al fluxului atunci când este solicitat. De exemplu, puteți da un număr aleatoriu de fiecare dată.
Exemplu:
Stream<Double> s = Stream.generate(Math::random);
Stream<T> Stream.concat(Stream<T> a, Stream<T> b)
metodă
Metoda concat()
concatenează cele două fluxuri trecute într- unul singur . Când datele sunt citite, acestea sunt citite mai întâi din primul flux și apoi din al doilea. Exemplu:
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. Filtrarea datelor
Alte 6 metode creează noi fluxuri de date, permițându-vă să combinați fluxuri în lanțuri (sau conducte) de complexitate diferită.
Stream<T> filter(Predicate<T>)
metodă
Această metodă returnează un nou flux de date care filtrează fluxul de date sursă în conformitate cu regula trecută . Metoda trebuie apelată pe un obiect al cărui tip este Stream<T>
.
Puteți specifica regula de filtrare folosind o funcție lambda , pe care compilatorul o va converti apoi într-un Predicate<T>
obiect.
Exemple:
Pârâie înlănțuite | Explicaţie |
---|---|
|
Rețineți numai numerele mai mici de trei |
|
Rețineți numai numerele mai mari decât zero |
Stream<T> sorted(Comparator<T>)
metodă
Această metodă returnează un nou flux de date care sortează datele din fluxul sursă . Treceți un comparator , care stabilește regulile pentru compararea a două elemente ale fluxului de date.
Stream<T> distinct()
metodă
Această metodă returnează un nou flux de date care conține doar elementele unice din fluxul de date sursă . Toate datele duplicate sunt eliminate. Exemplu:
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>)
metodă
Această metodă returnează un nou flux de date , deși datele din acesta sunt aceleași cu cele din fluxul sursă. Dar când următorul element este solicitat din flux, funcția pe care ați transmis-o peek()
metodei este apelată cu el.
Dacă treceți funcția System.out::println
la peek()
metodă, atunci toate obiectele vor fi afișate când vor trece prin flux.
Stream<T> limit(int n)
metodă
Această metodă returnează un nou flux de date care conține doar primele n
elemente din fluxul de date sursă . Toate celelalte date sunt eliminate. Exemplu:
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)
metodă
Această metodă returnează un nou flux de date care conține toate aceleași elemente ca și fluxul sursă , dar omite (ignoră) primele n
elemente. Exemplu:
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