Stream

Stream ehk voog on andmete liikumise kanal. Igast kollektsioonist saab moodustada voo, mille peal on võimalik opereerida kasutades Stream API't. Selle kasutamine suurendab loetavust ja vähendab boilerplate. Sellega kaob vajadus tsüklite järele.

Streamil on kahte tüüpi operatsioone:

  • Intermediate operatsioonid on laisad ja tagastavad uue striimi. Näiteks filter() kasutamine ei hakka kohe midagi filtreerima, vaid loob uue voo. Uut voogu läbides näeb seal ainult predikaadile vastavaid ehk filtreeritud elemente.

  • Terminal operatsioonid on entusiastlikud ja võivad voo läbi käia tekitades mingit kõrvalmõju või saades kätte mingi tulemuse. Sellised on näiteks collect() ja sum(). Peale terminal operatsiooni ei saa striimi enam kasutada.

Näited intermediate operatsioonidest

.filter() tagastab uue striimi, mis sisaldab ainult elemente, mis vastavad predikaadile. seejärel loeb .count() kokku elementide arvu.

long numbersHigherThanFive = Stream.of(7, 2, 3, 5, 6)
                .filter(n -> n > 5)
                .count();
        System.out.println(numbersHigherThanFive); // 2

.map() rakendab etteantud funktsiooni igale elemendile. Vaata ka mapToInt(), mapToLong() ja mapToDouble()

List<Integer> words = Stream.of("a", "ab", "abc")
        .map(s -> s.length()) // with method reference .map(String::length)
        .collect(Collectors.toList());

.peek() hea vahend debug info kogumiseks jaoks.

List<String> names = Stream.of("Bob", "tim", "Alice")
        .filter(s -> Character.isUpperCase(s.charAt(0)))
        .peek(s -> System.out.println(s)) // with method reference .peek(System.out::println)
        .map(s -> s.toUpperCase()) // with method reference .map(String::toUpperCase)
        .collect(Collectors.toList());

.distinct() jätab equals() alusel alles ainult unikaalsed elemendid. .limit() paneb piiri elementide arvule. .sorted() sorteerib elemendid kasvavas järjestuses.

List<Integer> numbers = Stream.of(3, 2, 4, 4, 4, 1)
        .distinct()
        .limit(3)
        .sorted()
        .collect(Collectors.toList());

Näited terminal operatsioonidest

.forEach() opereerib igal elemendil voos sarnaselt tavalise for loopiga.

Stream.of("a", "b").forEach(e -> System.out.println(e)); // .forEach(System.out::println)

.reduce() kombineerib voo elemendid BinaryOperator alusel.

int sum = IntStream.of(7, 3).reduce(0, (a, b) -> a + b);

collect() loob voost mingi kollektsiooni (näiteks listi).

List<String> filtered = Stream.of("ok", "yup")
        .filter(s -> s.startsWith("o"))
        .collect(Collectors.toList());

max() ja min() leiavad vastavalt maksimaalse ja minimaalse väärtuse.

OptionalInt max = IntStream.of(6, 7, 8).max();
OptionalInt min = IntStream.of(6, 7, 8).min();

.anyMatch(), allMatch(), noneMatch() vaatavad voos vähemalt ühe elemendi, kõike elementide või mitte ühtegi elemendi vastavust predikaadile.

List<String> days = Arrays.asList("Saturday", "Monday", "Friday", "Sunday");

boolean anyDayStartWithS = days.stream().anyMatch(s -> s.startsWith("S"));
boolean allStartWithM = days.stream().allMatch(s -> s.startsWith("M"));
boolean noneStartWithY = days.stream().noneMatch(s -> s.startsWith("Y"));

Optional<String> anyString = days.stream().findAny();

Erinevaid näiteid

Täisarvude järjendi saamine vahemiku (range) algusel:

Täisarvude järjend vahemikus ja kindla sammuga:

List<Integer> numbers = IntStream.iterate(0, i -> i + 3)
                        .takeWhile(i -> i < 100)
                        .boxed()
                        .collect(Collectors.toList());

Sõnede järjestamine pikkuse järgi:

List<String> languages = List.of("python", "ruby", "java", "c++", "lisp", "english");
List<String> sortedLanguages = languages.stream()
        .sorted(Comparator.comparingInt(String::length))
        .collect(Collectors.toList());

Sõnede järjestamine pikkuse järgi (suuremast väiksemaks), seejärel tähestiku järgi (väiksemast suuremaks):

List<String> languages = List.of("python", "ruby", "java", "c++", "lisp", "english");
List<String> sortedLanguages = languages.stream()
        .sorted(
                Comparator.comparingInt(String::length).reversed()
                        .thenComparing(String::compareTo))
        .collect(Collectors.toList());

Sõnede järjestamine pikkuse järgi (suuremast väiksemaks), seejärel tähestiku järgi (suuremast väiksemaks):

List<String> languages = List.of("python", "ruby", "java", "c++", "lisp", "english");
List<String> sortedLanguages = languages.stream()
        .sorted(
                Comparator.comparing(String::length, Comparator.reverseOrder())
                .thenComparing(p -> p, Comparator.reverseOrder())
        )
        .collect(Collectors.toList());

Loendame tekstist erinevate sõnade kogused Map andmetüüpi:

String text = "hello world world hello hi world";
Map<String, Long> wordCounts = Arrays.stream(text.split(" ")).
        collect(Collectors.groupingBy(s -> s, Collectors.counting()));

Java 8 Streams walkthrough/cheat sheet:

http://files.zeroturnaround.com/pdf/zt_java8_streams_cheat_sheet.pdf

Dokumentatsioon:

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html