a programozás alapjai 3
TRANSCRIPT
1
A programozás
alapjai 3.
Java kollekció-streamek
Goldschmidt Balázs
Ez az oktatási segédanyag a Budapesti Műszaki és
Gazdaságtudományi Egyetem oktatója által
kidolgozott szerzői mű. Kifejezett felhasználási
engedély nélküli felhasználása szerzői jogi
jogsértésnek minősül.
◼ Feladat:
hallgatói listából válasszuk ki a 30 évnél idősebbeket
és adjuk össze, hogy mennyi sört tudnak meginni!
Kollekciók feldolgozása
Basics of programming 3 © BME IIT, Goldschmidt Balázs 2
List<Student> l = ...;double cap = 0.0;LocalDate now = LocalDate.now();
for (Student s : l) {if (s.getBirthDate().until(now).getYears() > 30) {
cap += s.getBeerCap();}
}
System.out.println(cap);
számítás
inicializálás
válogatásátalakítás
1
2
2
◼ Mik voltak az elemi lépések?
inicializálás
◼ összegzéshez kell egy kezdő elem (0.0)
a kollekción szűrés
◼ kiválogatjuk a nekünk kellő elemeket
a kiválasztott elemek átalakítása
◼ hallgatóból sörmennyiség (Student → double, getBeerCap())
összegzés (akkumuláció)
◼ az elemek helyett közös jellemző áll elő
Bontsuk szét elemeire!
Basics of programming 3 © BME IIT, Goldschmidt Balázs 3
◼ Cél: elemek egységes kezelése
Kollekciót lehessen szűrni
◼ filter: kollekció → kollekció
Kollekció elemein lehessen egyedi leképzést csinálni
◼ map: elem → másik elem
Kumulált érték előállítása
◼ reduce: kollekció → valami más
◼ Hatékonyabb működés
párhuzamos végrehajthatóság miatt
Filter-Map-Reduce módszer
Basics of programming 3 © BME IIT, Goldschmidt Balázs 4
párhuzamosítható,
ha állapotmentes
párhuzamosítható,
ha állapotmentes
párhuzamosítható,
ha asszociatív
3
4
3
◼ Feladat:
hallgatói listából válasszuk ki a 30 évnél idősebbeket
és adjuk össze, hogy mennyi sört tudnak meginni!
Stream alapjai
Basics of programming 3 © BME IIT, Goldschmidt Balázs 5
List<Student> l = ...;LocalDate now = LocalDate.now();
double cap = l.stream().filter(s->(s.getBirthDate().until(now)
.getYears() > 30)).map(Student::getBeerCap).reduce(0.0, Double::sum)
;System.out.println(cap);
forrás
átalakítás
összegzés
válogatás
◼ Adatszerkezet: stream
objektumfolyam
◼ lehet DoubleStream, IntStream, LongStream is
lehet párhuzamos vagy sorrendi
◼ parallel vs. sequential
◼ a feldolgozás végezhető-e párhuzamosan
a tartalma jöhet bárhonnan
◼ kollekció, hálózat, más stream-ek, …
◼ Feldolgozás: pipeline (forrás) – (köztes művelet)* – (záró művelet)
(source) – (intermediate op)* – (terminal op)
Stream alapfogalmak
Basics of programming 3 © BME IIT, Goldschmidt Balázs 6
lusta
kiértékelés
5
6
4
◼ Tömeges feldolgozás
kollekcióra és végtelen forrásra egyaránt
◼ De: a hagyományos kollekció egyedi hozzáférésre jobb!
◼ Hatékonyság
könnyen párhuzamosítható
◼ állapotmentes (stateless) műveletek előnyben
◼ asszociatív összegzések (redukciók) előnyben
jól definiált elemi műveletek
◼ minden elemre egységesen
◼ olvasható, karbantartható
Stream előnyei
Basics of programming 3 © BME IIT, Goldschmidt Balázs 7
◼ Pipeline feldolgozás
(forrás) – (köztes művelet)* – (záró művelet)
◼ pl. l.stream().filter(x).map(y).reduce(z);
Forrás
◼ kollekcióból, hálózatról, generátorral stb.
Köztes: szűrések/leképezések (filter/map)
◼ tetszőleges számú
◼ közös be-kimenő típusaik kompatibilisek legyenek
◼ lusta kiértékelés
Záró: redukció
◼ lezárja a streamet
◼ a stream nem használható újra
Stream használata
Basics of programming 3 © BME IIT, Goldschmidt Balázs 8
7
8
5
Stream-pipeline
Basics of programming 3 © BME IIT, Goldschmidt Balázs 9
M1 M2 M3F1S1 S2 S3 S4 S5 RForrás
Stream létrehozása
Basics of programming 3 © BME IIT, Goldschmidt Balázs 10
M1 M2 M3F1Forrás S1 S2 S3 S4 S5 R
9
10
6
◼ Collection<T>-ből stream()
◼ szekvenciális feldolgozású
parallelStream()
◼ párhuzamos feldolgozású
◼ Tömbből Arrays.stream(T[] t, int startIncl, int endExcl)
int, long, double típusra is
Stream létező elemekből
Basics of programming 3 © BME IIT, Goldschmidt Balázs 11
Stream<String> s = Arrays.stream(args);
◼ Stream<T> statikus metódusaival empty()
◼ üres stream
concat(Stream<? ext T> a, Stream<? ext T> b)
◼ meglevő két streamet összefűz
of(T t)
of(T... values)
◼ egy/több T-ből stream
Stream létező elemekből
Basics of programming 3 © BME IIT, Goldschmidt Balázs 12
Stream<String> s = Stream.of("hello", "world");
11
12
7
◼ Stream<T> statikus metódusaival generate(Supplier<T> s)
◼ visszatér egy s által biztosított tartalmú streammel
◼ T s.get() metódust hívva
◼ végtelen hosszú
Stream generálása
Basics of programming 3 © BME IIT, Goldschmidt Balázs 13
// kockadobások végtelen sorbanRandom rnd = new Random();Stream<Integer> s = Stream.generate(()->(rnd.nextInt(6)+1));
◼ Stream<T> statikus metódusaival iterate(T seed, Predicate<? super T> hasNext,
UnaryOperator<T> f))
◼ seed kezdőértékkel
◼ opcionális hasNext-ig,
◼ f függvény ismételt meghívása
◼ elemei: seed, f(seed), f(f(seed)), f(f(f(seed))), …
Stream generálása
Basics of programming 3 © BME IIT, Goldschmidt Balázs 14
// 2 hatványai: 1, 2*1=>2, 2*2=>4, 2*4=>8, ...Stream<Integer> s = Stream.iterate(1, x->2*x);
13
14
8
Stream köztes műveletei
Basics of programming 3 © BME IIT, Goldschmidt Balázs 15
M1 M2 M3F1Forrás S1 S2 S3 S4 S5 R
Stream<T>-n, az eredmény is Stream<T>
◼ állapotmentes (stateless) műveletek
filter(Predicate<? super T> predicate)
◼ a predikátumnak megfelelő elemek maradnak meg
◼ példa: számoljuk meg a 3,5 átlagnál jobbakat
Szűrő metódusok (köztes)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 16
long cnt = l.parallelStream().filter(s-> (s.getAverage()>3.5)).count();
Student → boolean
15
16
9
Stream<T>-n, az eredmény is Stream<T>
◼ állapottal rendelkező (stateful) műveletek
sorted([Comparator<? super T> comparator])
◼ elemek rendezve
◼ példa: írjuk ki fordított átlag (azon belül névsor) szerint
Szűrő metódusok (köztes)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 17
l.stream().sorted(Comparator.comparing(Student::getAverage)
.reversed()
.thenComparing(Student::getName)).forEach(System.out::println);
Kaszkád
komparátor
Stream<T>-n, az eredmény is Stream<T>
◼ állapottal rendelkező (stateful) műveletek
distinct()
◼ ismétlődések elhagyása (equals alapján)
limit(long maxSize)
◼ legfeljebb maxSize-nyi elem megtartása
skip(long n)
◼ az első n elem eldobása
Szűrő metódusok (köztes)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 18
17
18
10
Stream<T>-n, az eredmény Stream<R>
◼ a leképzés egy T-ből egy R-t csinál
◼ állapotmentes (stateless) műveletek
map(Function<? super T, ? extends R> mapper)
◼ a mapper-nek megfelelő leképzés
◼ egy-egyes leképezésekre (pl. ember és a sörkapacitása)
mapToInt(ToIntFunction<? super T> mapper)
◼ a mapper T-ből int-et csinál, visszatérés IntStream
IntStream dedikált stream int típusra
◼ fentiek long és double típusra is
Leképző metódusok (köztes)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 19
Példa: sörkapacitás logaritmusát összegezzük
Leképző metódusok (köztes)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 20
double cap = l.parallelStream().map(s->Math.log(s.getBeerCap())).reduce(0.0, Double::sum)
;
Student → double
double cap = l.parallelStream().mapToDouble(s->Math.log(s.getBeerCap())).sum()
;DoubleStream metódusa
19
20
11
Stream<T>-n, az eredmény Stream<R>
◼ a leképezés egy-több típusú (T → Stream<R>)
◼ állapotmentes (stateless) műveletek
flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
◼ flatMap egyesíti az eredmény-streameket
◼ van flatMapToInt, flatMapToLong és flatMapToDouble is
eredmény IntStream, LongStream illetve DoubleStream
Leképző metódusok (köztes)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 21
List<String> lines = ...;long words = lines.parallelStream().flatMap(s->Arrays.stream(s.split(" "))).count();
sorokból szavak
Stream redukciója
Basics of programming 3 © BME IIT, Goldschmidt Balázs 22
M1 M2 M3F1Forrás S1 S2 S3 S4 S5 R
21
22
12
T reduce(T identity, BinaryOperator<T> acc)
◼ összegzés identity kezdőértékkel
◼ acc összegez (accumulator)
◼ elvi működés:
Redukáló metódusok (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 23
T result = identity;for (T element : this_stream)
result = acc.apply(result, element);return result;
double cap = l.stream().map(Student::getBeerCap).reduce(0.0, Double::sum);
Optional<T> reduce(BinaryOperator<T> acc))
◼ összegzés null kezdőértékkel
◼ acc összegez
◼ Optional<T> vagy üres, vagy az eredmény van benne
eredmény: T get() metódussal lekérhető; kivételt dob, ha nincs
van-e eredmény: boolean isPresent()
Redukáló metódusok (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 24
Optional<Double> cap = l.stream().map(Student::getBeerCap).reduce(Double::sum);
double c = cap.get();
23
24
13
néha egyszerűbb összevonni a szűrést és az
összegzést
Összetett reduce (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 25
double cap = l.stream().map(s->Math.log(s.getBeerCap())).reduce(0.0, Double::sum)
;
double cap = l.stream().reduce(0.0,
(sum, s)->sum+Math.log(s.getBeerCap()),Double::sum)
;
Részeredmény
számítása
Részeredmények
kombinálása
Studentek
Student → double
double-k összeadása
void forEach(Consumer<? super T> action))
void forEachOrdered(Consumer<? super T> action))
◼ végrehajtja action-t minden elemen
◼ az ordered determinisztikusan, sorrendben
◼ elvi működés:
Feldolgozó metódusok (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 26
for (T element : this_stream)action.apply(element);
l.stream().forEach(System.out::println);
25
26
14
Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)
◼ max/min érték keresése
◼ Optional<T> vagy üres, vagy az eredmény van benne
eredmény: T get() metódussal elérhető
van-e eredmény: boolean isPresent()
Redukáló metódusok (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 27
Optional<Double> cap = l.stream().map(Student::getBeerCap).max(Double::compare);
double c = cap.get(); általános
megoldás
boolean allMatch(Predicate<? super T> predicate)
boolean anyMatch(Predicate<? super T> predicate)
boolean noneMatch(Predicate<? super T> predicate)
◼ a feltételnek megfelel-e mindegyik, legalább egy, egy sem
long count()
◼ az elemek száma
IntStream, LongStream, DoubleStream esetén
average(), max(), min()
◼ átlag, maximum, minimum
Redukáló metódusok (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 28
OptionalDouble cap = l.stream().mapToDouble(Student::getBeerCap).max();
27
28
15
Pipeline belső állapota
Basics of programming 3 © BME IIT, Goldschmidt Balázs 29
M1 M2 M3F1Forrás S1 S2 S3 S4 S5 R
Stream<T> peek(Consumer<? super T> action)
◼ minden elemen lefut az action
◼ nem módosítja a streamet
◼ alapvetően debug célból
Pipeline belső állapota (köztes)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 30
double cap = l.stream().filter(s->(s.getBirthDate().until(now)
.getYears() > 30)).peek(System.out::println).map(Student::getBeerCap).reduce(0.0, Double::sum)
;
29
30
16
Streamből Collection
Basics of programming 3 © BME IIT, Goldschmidt Balázs 31
M1 M2 M3F1Forrás S1 S2 S3 S4 S5 R Cél
◼ Feladat
meglevő stream-ből a 30 évnél idősebb hallgatók
listáját kinyerni
Streamből Collection
Basics of programming 3 © BME IIT, Goldschmidt Balázs 32
List<Student> l = ...;LocalDate now = LocalDate.now();
List<Student> l30 = l.stream().filter(s->(s.getBirthDate().until(now)
.getYears() > 30)).collect(Collectors.toList());
31
32
17
◼ Kigyűjtés R collect(Collector<? super T, A, R> collector)
◼ a collector segítségével R (egy kollekció) előállítása
◼ Collector intefészben több metódus
T: a stream-beli elemek típusa
A: átmeneti tároló típusa
◼ általában nem ismerjük, ?-lel jelölik
◼ pl. stringek összefűzésekor A nem String, mert drága lenne
R: a visszaadott kollekció típusa
Streamből Collection (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 33
◼ Collectors segédosztály (utility class) Collector<T,?,List<T>> toList()
Collector<T,?,Set<T>> toSet()
Collector<T,?,C> toCollection(Supplier<C> cFac)
◼ lista, halmaz vagy más kollekció generálása
◼ cFac legyárt egy C típusú kollekciót
ahol C extends Collection<T>
Streamből Collection (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 34
List<Student> l2 = l.stream().filter(s->(s.getBirthDate().until(now)
.getYears() > 30)).collect(Collectors.toList());
33
34
18
◼ Collectors segédosztály (utility class) Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper)
◼ Map létrehozása és feltöltése
◼ példa: nevekhez sörkapacitás Map-ben
Streamből Map (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 35
Map<String,Double> bm1 = l.stream().collect(Collectors.toMap(Student::getName,
Student::getBeerCap));
Map<String,Double> bm2 = l.stream().collect(Collectors.toMap(Student::getName,
s->Math.log(s.getBeerCap())));
◼ Collectors segédosztály (utility class) Collector<T,?,Map<K,List<T>>>groupingBy(Function<? sup T,? ext K> classifier)
◼ Map létrehozása és feltöltése
◼ példa: sörkapacitásokhoz hallgatók Map-ben
Streamből Map (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 36
Map<Double,List<Student>> bs = l.stream().collect(Collectors
.groupingBy(Student::getBeerCap));
35
36
19
◼ Collectors segédosztály (utility class) Collector<T,?,Map<K,D>>groupingBy(Function<? sup T,? ext K> classifier,
Collector<? super T,A,D> subcollect)
◼ Map létrehozása és feltöltése
◼ az egyes kulcsokhoz tartozó elemek külön-külön kollektálásával
◼ példa: születési év alapján átlagos sörkapacitás Map-ben
Streamből Map (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 37
Map<Integer,Double> yavg = l.stream().collect(Collectors
.groupingBy(s->s.getBirthDate().getYear(),Collectors.averagingDouble(Student::getBeerCap)
));
◼ Collectors segédosztály (utility class) Collector<CharSequence,?,String>joining([CharSequence delimiter, [CharSequence prefix, CharSequence suffix]]))
◼ String létrehozása a stream tartalmából összefűzéssel
◼ példa: nevek összefűzése újsorjellel
Streamből String (záró)
Basics of programming 3 © BME IIT, Goldschmidt Balázs 38
String names = l.stream().map(Student::getName()).collect(Collectors.joining("\n"));
37
38
20
◼ Feladat:
hallgatói listából válasszuk ki a 30 évnél idősebbeket
átlagosan mennyi sört tudnak meginni?
Számoljunk átlagot!
Basics of programming 3 © BME IIT, Goldschmidt Balázs 39
List<Student> l = ...;LocalDate now = LocalDate.now();
double avg = l.stream().filter(s->(s.getBirthDate().until(now)
.getYears() > 30)) .collect(Collectors
.averagingDouble(Student::getBeerCap));
System.out.println(avg); Collector-ral
◼ Feladat:
hallgatói listából válasszuk ki a 30 évnél idősebbeket
átlagosan mennyi sört tudnak meginni?
Számoljunk átlagot!
Basics of programming 3 © BME IIT, Goldschmidt Balázs 40
List<Student> l = ...;LocalDate now = LocalDate.now();
OptionalDouble avg = l.stream().filter(s->(s.getBirthDate().until(now)
.getYears() > 30)) .mapToDouble(Student::getBeerCap).average();
System.out.println(avg);DoubleStream-mel
39
40
21
◼ Soros vagy párhuzamos és egyebek boolean isParallel()
◼ megadja, hogy párhuzamosan dolgozzuk-e fel
Stream<T> parallel()
Stream<T> sequential()
◼ visszaad egy azonos tartalmú streamet,
ami párhuzamos/soros feldolgozású
Stream<T> unordered()
◼ visszaad egy azonos tartalmú streamet,
ami rendezetlen
Iterator<T> iterator()
◼ iterátor a stream elemein
Stream jellege
Basics of programming 3 © BME IIT, Goldschmidt Balázs 41
Basics of programming 3 © BME IIT, Goldschmidt Balázs 42
Köszönöm a figyelmet!
41
42