Kolekce typu Stream
-
proud (
java.util.stream.Stream
) je typ zavedený od Java 8 -
neplést se vstupně/výstupními proudy (
java.io.InputStream
/OutputStream
) -
jde jednoduše řečeno o homogenní lineární strukturu prvků, tedy obdobně jako např. seznam nebo kolekce
-
hodí se pro snadné řetězení více operací nebo tam,
-
kde se jedná o zpracování dat, která v době spuštění nemusejí dosud (nebo vůbec nikdy) celá aktuálně existovat, nýbrž jsou dle potřeby generována
-
jedná se o určitý typ "lazy collections" na rozdíl od klasických seznamů apod., které všechny prvky aktuálně obsahují (odkazují se na ně) a kde to není tak, že by se prvek objevil, až teprve ho budeme potřebovat.
Kdy proud použít?
Bez splnění aspoň jedné následující charakteristiky nemají proudy zásadnější smysl:
-
využíváme řetězení operací
-
využíváme možnost paralelizace těchto operací (hromadné provedení nad prvky proudu), což je jediná cesta, jak na moderních vícejádrových strojích zvýšit výkon
-
pomůže to pro zpřehlednění programu, jeho zhutnění
Předpoklady využití
Proudy nelze prakticky příliš využít bez současného zvládnutí funkcionálních prvků v Java 8:
-
odkazy na funkce (metody) a
-
lambda výrazy.
Reálně bude ještě delší dobu trvat, než se začnou masověji objevovat v kódu:
-
neznalost u vývojářů
-
setrvačnost v běhových prostředích (možno až od Java 8 a ta skoro nikde není nasazena)
-
nezralost API (např. výkonnostní problémy u paralelizace v cloudových, ale i jiných prostředích)
Vytvoření Stream
-
Nejčastěji a nejjednodušeji z prvků pole nebo kolekce, tzn. např.
-
List<String> names = …; names.stream()
nebo -
String[] names = …; Stream.of(names)
-
-
Proudy pro následné paralelní zpracování lehce obdobně vytvoříme pomocí např.
names.parallelStream()
-
tento postup tedy nevede k vytvoření proudu, kde se prvky "vyrábějí" dle potřeby, ale kde již od počátku všechny jsou.
Příklad použití Stream
Dále lze proud použít pro řetězení operací nad jeho prvky:
List<String> names = ... names.stream().map(String::toUpperCase).forEach(System.out::println);
-
Nejdříve vytvoří ze seznamu proud
-
pak každý řetězec převede pomocí
toUpperCase
(průběžná operace) -
na závěr každý takto převedený řetězec vypíše (terminální/koncová operace)
Zhruba odpovídá sekvenční iteraci:
List<String> names = ... for(String name: names) { System.out.println(name.toUpperCase()); }
Stream
s lambda výrazy
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); integerList.parallelStream().forEach(i -> System.out.print(i + " "));
nebo s více stupni řetězení
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); integerList .parallelStream() .filter(i -> isOdd(i)) .forEach(i -> System.out.print(i + " "));
Dokumentace
-
Benjamin Winterberg: Java 8 Stream Tutorial
-
Amit Phaltankar: Understanding Java 8 Streams API