Lisamaterjalid 9
Stream API
Sel nädalal tutvusite failide lugemise ja kirjutamisega ning sisend- ja väljundvoogudega. Javas eksisteerivad nendest täiesti eraldi paketi
java.util.stream
vood, mille abil saab töödelda andmestruktuure, nagu kogumeid ja massiive. Selleks kasutatakse funktsionaalsele programmeerimisele omaseid operatsioone, mis jagunevad kolmeks. Esimesed loovad voo, järgmised tagastavad voo (intermediate operation) ning viimased tagastavad või väljastavad voo põhjal mingi tulemuse (terminal operation). Vooge tagastavaid operatsioone võib olla järjest mitu, aga teisi meetodeid saab ainult üks kord rakendada. Vood on laisad ehk operatsioone tehakse alles siis ja täpselt nii palju, kui on vaja, et tulemust saada.
Paljud voo meetodid nõuavad oma parameetriteks funktsiooniliideseid, millega saab tutvuda eelmise nädala lisamaterjalide lambda-avaldiste teema all. Kui lambda ülesanded piirduvad meetodi kutsega, on võimalik lambdat väljendada lühemalt (method reference). Näiteks lambda n -> System.out.println(n)
asemel saab kirjutada System.out::println
.
Kõige üldisem liides voogude kasutamiseks on
Stream
ning täis- ja ujukomaarvudega töötamiseks on olemas veel
IntStream
, LongStream
ja
DoubleStream
. Voogude loomiseks on mitmeid võimalusi. Kogumitel ja massiividel eksisteerib selleks meetod stream
, aga liideses Stream
on defineeritud hulk staatilisi meetodeid:
of
– võtab argumendiks ühe või mitu elementi ja tagastab nende voo;empty
– tagastab tühja voo;iterate
– tagastab voo, millel on algne defineeritud element ning järgnevad liikmed on lambda-avaldise rakendamise tulemused;generate
– sarnaselt meetodileiterate
tagastab see voo, aga algset elementi pole. Seetõttu ei saa luua näiteks voogu, kus iga järgnev element on kaks korda suurem eelmisest. Samas on see mugav samade või suvaliselt genereeritud elementidega voo loomiseks;builder
– loobStream.Builder
isendi, millele saab elemente lisada meetodigaadd
. Seda tagastatakse voona meetodibuild
abil.
Hulk meetodeid, mis tagastavad voo:
distinct
– tagastab duplikaatideta voo;filter
– tagastab voo, mille elemendid vastavad argumendiks antud predikaadile;map
– tagastab argumendiks antud funktsiooni töödeldud elementide voo. Voo tüübi muutmiseks on olemas meetodidmapToInt
,mapToLong
jamapToDouble
;peek
– tagastab muutmata kujul voo. See on peamiselt silumiseks mõeldud meetod, kus saab näiteks elemente väljastada;limit
– tagastab voo, mille maksimaalne pikkus on argumendiks antud arv;skip
– tagastab voo, milles on eemaldatud esimesed n arvu;sorted
– tagastab sorteeritud voo. Kui klass on realiseerinud liidestComparable
, saab sorteerida selle alusel. Teise variandina kasutatakse argumendiks antud funktsiooniliidestComparator
.boxed
– tagastabIntStream
-i põhjal täisarvude voo.
Mõned meetodid, mis ei tagasta voogu:
reduce
– tagastab voo elementide põhjal mingi tulemuse. Näiteks saab arvutada elementide koguarvu või numbrite summa;count
– tagastab voo elementide arvu;max
,min
– tagastab vastavalt voo suurima või vähima väärtuse kasutades funktsiooniliidestComparator
;forEach
– läbib voo iga elemendi ja teeb nendega midagi vastavalt lambda-avaldisele;collect
– kogub voo elemendid muutuva suurusega andmestruktuuri ja tagastab tulemuse.toArray
– tagastab kõigi voo elementidega massiivi;toList
– tagastab kõigi voo elementidega listi.
Esimeses näites muudetakse täisarvudest koosnev list vooks meetodiga stream
. Sellest eemaldatakse korduvad arvud meetodiga distinct
ja väärtused, mis on väiksemad 11-st meetodiga filter
. Seejärel sorteeritakse voog kahanevas järjekorras andes meetodi sorted
argumendiks Comparator.reverseOrder
. Lõpuks luuakse voost täisarvude list meetodiga toList
:
List<Integer> arvud = Arrays.asList(3, 63, 1, 20, 2, 10, 33, 63); List<Integer> töödeldudArvud = arvud.stream() .distinct() .filter(arv -> arv > 10) .sorted(Comparator.reverseOrder()) .toList(); System.out.println(töödeldudArvud);Väljastatakse:
[63, 33, 20]
Staatilise meetodiga of loodud täisarvude voog järjestatakse kasvavas järjekorras meetodiga sorted
. Sellele järgneb elementide väljastamine meetodiga peek
ja voo IntStream
-ks muutmine meetodiga mapToInt
, et meetod toArray
tagastaks Object[]
asemel int[]
:
Stream<Integer> voog = Stream.of(7, 6, 9, 1); int[] list = voog.sorted() .peek(n -> System.out.println(n)) .mapToInt(i -> i) .toArray();Väljastatakse:
1 6 7 9
Elementidega voogu saab genereerida meetodiga iterate
. Voo esimene liige on 1 ja kõik järgnevad arvud on eelneva arvu kahekordsed. Elementide kogust tuleb piirata näiteks meetodiga limit
, et uusi arve ei tuleks lõpmatuseni. Kui piiramist ei toimu, saab kuhi täis ja visatakse OutOfMemoryError
. Tulemus väljastatakse meetodiga forEach
, kus kasutatakse lambda lühendatud vormi:
Stream.iterate(1, n -> n + n) .limit(4) .forEach(System.out::println);Väljastatakse:
1 2 4 8
Meetodi iterate
elementide genereerimist saab veel piirata lisaargumendiks antud predikaadiga. Selles näites kontrollitakse, kas arv on väiksem või võrdne kaheksaga. Loodud voos arvutatakse voo liikmete summa meetodis reduce
:
int arv = Stream.iterate(1, n -> n <= 8, n -> n + n) .reduce(0, (kokku, n) -> kokku + n); System.out.println(arv);Väljastatakse:
15
Meetod generate
loob nullidega voo ja limit
tõkestab selle liikmete arvu viieni. Liikmete arvu tagastab meetod count
:
long nulleKokku = Stream.generate(()-> 0).limit(5).count(); System.out.println(nulleKokku);Väljastatakse:
5