Ce face Stream
Un Stream<T> este o „bandă rulantă” de valori de tip T. Nu păstrează datele (nu este o colecție), ci le trece prin mai multe etape: filter (păstrează doar unele), map (le transformă), sorted (le ordonează) și, la final, o operație care produce rezultatul: toList(), count(), collect(...), forEach(...).
Etapele din mijloc se numesc operații intermediare: returnează un nou stream și nu fac nimic până nu apare operația finală (stream-ul este „leneș”). Un stream se poate parcurge o singură dată; după operația finală trebuie creat altul.
Cel mai des obții un stream dintr-o colecție, cu lista.stream(), sau direct din valori, cu Stream.of(...). Pentru numere există variantele IntStream, LongStream și DoubleStream, care au sum(), average() și nu împachetează numerele în obiecte.
Ce returnează
Este o interfață. Metodele intermediare returnează un nou Stream; metodele finale returnează rezultatul (o listă, un număr, un Optional) sau nimic (forEach).
Exemple
Exemplul 1: Bucla clasică și varianta cu stream
Ambele variante aleg orașele cu mai mult de 4 litere, le scriu cu majuscule și le sortează. Varianta cu stream descrie ce vrei, nu cum se parcurge lista.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> orase = List.of("Iași", "Cluj", "Brașov", "Arad", "Timișoara", "Galați");
// cu buclă
List<String> rezultat = new ArrayList<>();
for (String oras : orase) {
if (oras.length() > 4) {
rezultat.add(oras.toUpperCase());
}
}
Collections.sort(rezultat);
System.out.println(rezultat);
// cu stream
List<String> cuStream = orase.stream()
.filter(oras -> oras.length() > 4)
.map(String::toUpperCase)
.sorted()
.toList();
System.out.println(cuStream);
}
}
Rezultat
[BRAȘOV, GALAȚI, TIMIȘOARA]
[BRAȘOV, GALAȚI, TIMIȘOARA]
Exemplul 2: Stream-ul este leneș
Fără operația finală, filter nu rulează deloc. Abia count() pornește prelucrarea.
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream<String> nume = Stream.of("Ana", "Mihai", "Ioana")
.filter(n -> {
System.out.println("verific " + n);
return n.startsWith("I");
});
System.out.println("Stream creat, încă nu s-a verificat nimic.");
long cate = nume.count();
System.out.println("Nume cu I: " + cate);
}
}
Rezultat
Stream creat, încă nu s-a verificat nimic.
verific Ana
verific Mihai
verific Ioana
Nume cu I: 1
Exemplul 3: Un stream se folosește o singură dată
A doua operație finală pe același stream aruncă IllegalStateException. Dacă ai nevoie de date de mai multe ori, păstrează lista și creează câte un stream nou.
import java.util.List;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<Integer> note = List.of(9, 7, 10, 5);
Stream<Integer> s = note.stream();
System.out.println("Câte note: " + s.count());
try {
System.out.println(s.count());
} catch (IllegalStateException e) {
System.out.println("Eroare: " + e.getMessage());
}
// corect: un stream nou din listă
System.out.println("Note de trecere: " + note.stream().filter(n -> n >= 5).count());
}
}
Rezultat
Câte note: 4
Eroare: stream has already been operated upon or closed
Note de trecere: 4
Greșeli frecvente
- Refolosești un stream după o operație finală. Primești
IllegalStateException: stream has already been operated upon or closed. Creează-l din nou din colecție. - Scrii doar operații intermediare (
filter,map,peek) și te aștepți să se întâmple ceva. Fără o operație finală, nimic nu rulează. - Modifici colecția sursă în timp ce o parcurgi cu stream-ul (de exemplu
lista.addînforEach). Rezultatul esteConcurrentModificationExceptionsau un comportament greșit.
Comenzi înrudite
Stream.of: Creează un stream direct din câteva valori; ofNullable face un stream gol pentru null, iar empty un stream fără elemente.Stream.filter: Păstrează în stream doar elementele care îndeplinesc o condiție.Stream.map: Transformă fiecare element al stream-ului în altă valoare, eventual de alt tip.Stream.collect: Adună elementele într-un rezultat (listă, set, map, text) folosind un Collector, de obicei din clasa Collectors.Stream.toList: Adună elementele stream-ului într-o listă care nu se poate modifica, sau într-un tablou cu toArray.IntStream: Un stream de numere int simple, cu sum, average, min, max; IntStream.of îl creează direct din valori.Collectors: Clasa cu rețete gata făcute pentru collect: liste, mulțimi, map-uri, texte unite, grupări, numărări și medii.