Filtrare, parcurgerea elementelor și mapare
Parcurgerea elementelor - Metoda forEach
Pentru parcurgerea elementelor unui flux se utilizează metoda forEach(), care reprezintă o operație terminală. Ca parametru, aceasta acceptă un obiect de tip Consumer<? super String>, care definește acțiunea ce va fi efectuată pentru fiecare element din set. De exemplu:
Stream<String> citiesStream = Stream.of("Paris", "Londra", "Madrid", "Berlin", "Bruxelles");
citiesStream.forEach(s -> System.out.println(s));
Aceasta va fi echivalentă cu parcurgerea tuturor elementelor într-un ciclu for și executarea acțiunii pentru fiecare element, adică afișarea la consolă. Rezultatul afișat pe consolă va fi:
Paris
Londra
Madrid
Berlin
Bruxelles
Putem simplifica utilizarea metodei forEach astfel:
Stream<String> citiesStream = Stream.of("Paris", "Londra", "Madrid", "Berlin", "Bruxelles");
citiesStream.forEach(System.out::println);
În acest caz, se transmite o referință la metoda statică care afișează șirul la consolă.
Filtrarea - Metoda filter
Pentru filtrarea elementelor din flux se folosește metoda filter(), care reprezintă o operație intermediară. Aceasta acceptă ca parametru o condiție sub forma unui obiect Predicate<T> și returnează un flux nou cu elementele care îndeplinesc această condiție:
Stream<String> citiesStream = Stream.of("Paris", "Londra", "Madrid", "Berlin", "Bruxelles");
citiesStream.filter(s -> s.length() == 6).forEach(s -> System.out.println(s));
Condiția s.length() == 6 returnează true pentru elementele a căror lungime este de 6 caractere. Astfel, rezultatul afișat va fi:
Londra
Madrid
Berlin
Să analizăm un alt exemplu de filtrare cu date mai complexe. Să presupunem că avem următoarea clasă Phone:
class Phone{
private String name;
private int price;
public Phone(String name, int price){
this.name=name;
this.price=price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
Filtrăm setul de telefoane în funcție de preț:
Stream<Phone> phoneStream = Stream.of(new Phone("iPhone 6 S", 54000), new Phone("Lumia 950", 45000),
new Phone("Samsung Galaxy S 6", 40000));
phoneStream.filter(p -> p.getPrice() < 50000).forEach(p -> System.out.println(p.getName()));
Mapare - Metoda map
Maparea sau transformarea permite definirea unei funcții de conversie a unui obiect într-altul, adică obținerea unui element de un alt tip din elementul existent. Pentru mapare se folosește metoda map(), care are următoarea definiție:
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
Funcția transmisă în metoda map definește transformarea obiectelor de tip T în obiecte de tip R. În rezultat, se returnează un flux nou cu obiectele transformate.
Să folosim clasa Phone definită anterior și să efectuăm o transformare de la tipul Phone la tipul String:
Stream<Phone> phoneStream = Stream.of(new Phone("iPhone 6 S", 54000), new Phone("Lumia 950", 45000),
new Phone("Samsung Galaxy S 6", 40000));
phoneStream
.map(p -> p.getName()) // introducem în flux doar numele telefoanelor
.forEach(s -> System.out.println(s));
Operația map(p -> p.getName()) introduce în noul flux doar numele telefoanelor. Pe consolă vor fi afișate doar numele:
iPhone 6 S
Lumia 950
Samsung Galaxy S 6
Să efectuăm alte transformări:
phoneStream
.map(p -> "nume: " + p.getName() + " preț: " + p.getPrice())
.forEach(s -> System.out.println(s));
În acest caz, fluxul rezultat conține doar șiruri de caractere, iar numele sunt combinate cu prețurile.
Pentru transformarea obiectelor în tipuri Integer, Long, Double sunt definite metode speciale: mapToInt(), mapToLong() și mapToDouble().
Mapare plată - Metoda flatMap
Maparea plată se efectuează atunci când dintr-un element trebuie să obținem mai multe. Această operațiune este realizată de metoda flatMap():
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
De exemplu, în exemplul anterior afișăm numele telefonului și prețul acestuia. Dar dacă dorim să stabilim pentru fiecare telefon un preț fără reducere și unul cu reducere? Adică dintr-un obiect Phone să obținem două obiecte cu informații, de exemplu, sub formă de șiruri de caractere. Pentru aceasta, utilizăm flatMap():
Stream<Phone> phoneStream = Stream.of(new Phone("iPhone 6 S", 54000), new Phone("Lumia 950", 45000),
new Phone("Samsung Galaxy S 6", 40000));
phoneStream
.flatMap(p -> Stream.of(
String.format("nume: %s preț fără reducere: %d", p.getName(), p.getPrice()),
String.format("nume: %s preț cu reducere: %d", p.getName(), p.getPrice() - (int)(p.getPrice() * 0.1))
))
.forEach(s -> System.out.println(s));
Rezultatul programului va fi:
nume: iPhone 6 S preț fără reducere: 54000
nume: iPhone 6 S preț cu reducere: 48600
nume: Lumia 950 preț fără reducere: 45000
nume: Lumia 950 preț cu reducere: 40500
nume: Samsung Galaxy S 6 preț fără reducere: 40000
nume: Samsung Galaxy S 6 preț cu reducere: 36000