FK~

Moje odkazy

Ostatní odkazy

Close Windows
Nenajdete mě na Facebooku ani Twitteru
Rozpad EU
Jsem členem FSF
There Is No Cloud …just other people's computers.
Sane software manifesto / Manifest příčetného softwaru

Java 8: Stream API

vydáno: 17. 1. 2015 23:23, aktualizováno: 9. 3. 2015 00:11

Dnes si na praktických příkladech ukážeme další novinky v Javě 8. Lambda výrazy, kterými jsme se zabývali minule, tu nejsou jenom pro okrasu – používají se ve standardní knihovně a umožňují elegantní práci s proudy objektů skrze nové Stream API.

Logo OpenJDK (průhledné 1)

Nejedná se o nízkoúrovňové vstupně-výstupní proudy (InputStream atd.) přenášející bajty, ale o objektové proudy generického typu, pomocí kterých pracujeme s daty na vyšší úrovni abstrakce. Pojďme se rovnou podívat na příklady.

Pro začátek získáme instanci Stream<T> z obyčejné kolekce. Projdeme všechny prvky a něco s nimi provedeme:

Collection<String> písmena = Arrays.asList(new String[]{"a", "b", "c"});

Stream<String> proud = písmena.stream();
proud.forEach(p -> System.out.println(p)); // p je typu String

Proud samozřejmě nemusíme přiřazovat do proměnné a můžeme zavolat forEach() rovnou, ale takhle je vidět datový typ, se kterým pracujeme. Ostatně: i když některé ukázky můžou na první pohled vypadat trochu magicky, žádná magie v tom není – vždy pracujeme s konkrétním datovým typem, instancí nějaké třídy (rozhraní), můžeme se podívat na JavaDoc a IDE nám bude napovídat metody. Pokud by něco nebylo jednoznačné, zdroják v Javě nepůjde zkompilovat.

Metoda forEach() přijímá jako argument instanci funkčního rozhraní Consumer<>. Tu jsme si v tomto případě vytvořili ad-hoc pomocí lambda výrazu, ale můžeme klidně využít i předem připravenou instanci v proměnné nebo se odkázat pomocí čtyřtečky na metodu nějaké třídy či objektu:

proud.forEach(System.out::println);

Další věc, kterou můžeme s proudy dělat jsou transformace – přemapujeme jednu hodnotu na jinou pomocí funkce. Např. změníme velikost písmen a nakonec zase vypíšeme na standardní výstup:

písmena.stream()
	.map(p -> p.toUpperCase())
	.forEach(s -> System.out.println(s));

Metoda map() nemění původní proud, ale vrací nám nový – ten může být i jiného typu – záleží na tom, co vrací daná funkce. Takto převedeme Stream<String> na Stream<Integer>:

Collection<String> pozdravy = Arrays.asList(new String[]{
	"ahoj", "nazdar", "dobrý den"});

pozdravy.stream()
	.map(s -> s.length())
	.forEach(i -> System.out.println("délka řetězce: " + i));

Proudy můžeme filtrovat – datový typ se v tomto případě nemění – jen vybíráme určitou podmnožinu prvků na základě predikátu:

Collection<Integer> n = Arrays.asList(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
n.stream().filter(i -> i % 2 == 0).forEach(i -> System.out.println("sudé: " + i));

Predikát je instance funkčního rozhraní Predicate<> a opět ji můžeme zadat funkcí nebo se na ni odkázat. Predikát přijímá na vstupu daný generický typ (nebo předka) a vrací logickou hodnotu.

Dalšími operacemi nad proudy jsou reduce()collect(). Ty se hodí na součty a obecně agregační funkce. Prostý součet čísel:

int součet = n.stream().reduce((a, b) -> a + b).get();

Součet délek řetězců:

int d = pozdravy.stream().collect(Collectors.summingInt((String s) -> s.length()));
System.out.println("Celková délka pozdravů: " + d);

Proudy nemusí být odvozené jen z kolekcí – můžeme je získat i jiných zdrojů a můžeme si je i sami generovat a postavit na nich nějaké vlastní API.

Např. proud Stream<Path> cest/souborů pocházející ze souborového systému – vyhledáme soubory s příponou .pdf a určitou velikostí:

Files.list(Paths.get("/tmp/"))
	.filter(p -> p.toString().endsWith(".pdf") && p.toFile().length() > 1024)
	.forEach(p -> System.out.println("Soubor: " + p));

Proudy se zpracovávají skutečně proudově-průběžně – můžeme mít i nekonečný proud dat, např. náhodná čísla. V tom případě se nám hodí metoda limit(), kterou omezíme počet zpracovávaných prvků:

Random r = new Random();
r.ints().limit(10).forEach(i -> System.out.println("náhodné číslo: " + i));

Nové proudové API v Javě 8 umožňuje psát více deklarativně – kód pak může připomínat spíše SQL (projekce, restrikce, agregační funkce…) než procedurální jazyky.

Odkazy a zdroje:

Témata: [softwarové inženýrství] [softwarová architektura] [Java]

Komentáře čtenářů

Tento článek zatím nikdo nekomentoval

Přidat komentář

reagujete na jiný komentář (zrušit)
jméno nebo přezdívka
název příspěvku
webová stránka, blog
e-mailová adresa
nápověda: možnosti formátování
ochrana proti spamu a špatným trollům

Náhled komentáře