Strumienie są odpowiedzią języka java na potrzebę tworzenia kodu umożliwiającego przetwarzanie równoległe. Java 8 Streams API udostępnia mechanizm przetwarzania kolekcji w Javie (np. Listy, Mapy, Sety). Podstawową ideą jest zamiana kolekcji w strumień danych, równoległe przetwarzanie tego strumienia i zbieraniu wyników tego przetwarzania na powrót w kolekcję danych.
Interfejs strumieni jest zdefiniowany w pakiecie java.util.stream. Od Javy 8 kolekcje posiadają metody zwracające strumienie. Podobnie do języków funkcyjnych strumienie wspierają operacje agregacji. Najpowszechniejsze operacje agregacji to filter, map, reduce, find, match, sort. Operacje te mogą być uruchamiane sekwencyjnie lub równolegle.
Jako wynik strumień może zwracać kolejny strumień. Metodę tworzenie łańcuchów strumieni, gdzie każdy poprzedni zwraca wejście do kolejnego nazywamy pipelining (montaż rurociągów).
Tradycyjnie w Javie do przechodzenia po kolkcjach korzystamy z iteratorów jawnie wyspecyfikowanych w kodzie:
List<String> names = new ArrayList<>();
for (Student student : students) {
if(student.getName().startsWith("A")){
names.add(student.getName());
}
}
Wykorzystując strumienie korzystamy z tzw. wewnętrznych iteracji, to znaczy logika iteracji po elementach jest dla nasz ukryta a skupiamy się na ich przetwarzaniu.
List<string> names = students.stream()
.map(Student::getName)
.filter(name->name.startsWith("A:)
.collect(Collectors.toList());
Operacja na strumieniach dzielimy na pośrednie i końcowe (terminalne). Operacje pośrednie zwracają jako wynik działania strumień. W powyższym przykładzie takimi operacjami są map,filter i limit. Operacje terminalne kończą przetwarzanie, zwykle agregują one wyniki, zliczają wartości, albo też nic nie wykonują (np. operacja foreach może być terminalna).
Dla operacji numerycznych stworzono specjalne strumienie IntStream, DobuleStream, and LongStream.
IntStream.rangeClosed(1, 10).forEach(num -> System.out.print(num));
// ->12345678910
IntStream.range(1, 10).forEach(num -> System.out.print(num));
// ->123456789
Stream stałych i elementów tablicy:
Stream.of("This", "is", "Java8", "Stream").forEach(System.out::println);
String[] stringArray = new String[]{"Streams", "can", "be", "created", "from", "arrays"};
Arrays.stream(stringArray).forEach(System.out::println);
Map
Zmienia przetwarzany element na coś innego, czyli pobiera element jednego typu i zwraca element innego typu. W praktyce najczęściej służy do wyciągnięcia jakiś atrybutów z obiektów.
students.stream()
.map(Student::getName)
.forEach(System.out::println);
The Student::getName jest skrótowym odwołaniem się do metody z klasy. Czyli na obiekcie typu Student, który przyjdzie ze streama wywołujemy metodę getName().
FlatMap
Działa podobnie jak mapa, tylko jako wynik zwraca strumień przetworzonych elementów.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<List<Integer>> mapped =
numbers.stream()
.map(number -> Arrays.asList(number -1, number, number +1))
.collect(Collectors.toList());
System.out.println(mapped); //:> [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
List<Integer> flattened =
numbers.stream()
.flatMap(number -> Arrays.asList(number -1, number, number +1).stream())
.collect(Collectors.toList());
System.out.println(flattened); //:> [0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5]
.
Filter
Wybiera elementy ze strumienia względem danego warunku
students.stream()
.filter(student -> student.getScore() >= 60)
.collect(Collectors.toList());
.
Distinct
Usuwa powtórzenia
students.stream()
.map(Student::getName)
.distinct()
.collect(Collectors.toList());
.
Limit
Ogranicza liczbę elementów w strumieniu do podanej liczby.
Sorted
Sortuje elementy w naturalnym dla nich porządku.
students.stream()
.map(Student::getName)
.sorted()
.collect(Collectors.toList());
Z pomocą klasy Comparator możemy definiować sortowania różnego typu
//Sorting names if the Students in descending order
students.stream()
.map(Student::getName)
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
//Sorting names if the Students in descending order
students.stream()
.map(Student::getName)
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
//Sorting students by First Name and Last Name both
students.stream()
.sorted(Comparator.comparing(Student::getFirstName).
thenComparing(Student::getLastName))
.map(Student::getName)
.collect(Collectors.toList());
//Sorting students by First Name Descending and Last Name Ascending
students.stream()
.sorted(Comparator.comparing(Student::getFirstName)
.reversed()
.thenComparing(Student::getLastName))
.map(Student::getName)
.collect(Collectors.toList());
Match
Match stosujemy gdy interesuje nas występowanie w strumieniu elementu o danych własnościach.
//Check if at least one student has got distinction
Boolean hasStudentWithDistinction = students.stream()
.anyMatch(student -> student.getScore() > 80);
//Check if All of the students have distinction
Boolean hasAllStudentsWithDistinction = students.stream()
.allMatch(student -> student.getScore() > 80);
//Return true if None of the students are over distinction
Boolean hasAllStudentsBelowDistinction = students.stream()
.noneMatch(student -> student.getScore() > 80);
.
Find
Pozwala znajdować obiekt w strumieniu o danych włanościach, możemy wybrać dowolny dopasowany obiekt lub pierwszy dopasowany.
//Returns any student that matches to the given condition
students.stream().filter(student -> student.getAge() > 20)
.findAny();
//Returns first student that matches to the given condition
students.stream().filter(student -> student.getAge() > 20)
.findFirst();
.
Reduce
Pozwala zredukować strumień danych do pojedynczej wartości.
//Summing without passing an identity
Optional<integer> sum = numbers.stream()
.reduce((x, y) -> x + y);
//Product without passing an identity
Optional<integer> product = numbers.stream()
.reduce((x, y) -> x * y);
.
Collect
Zbiera elementy strumienia w listę.
Mała ściąga dotycząca strumieni
Przykłady po Polsku
Więcej przykładów po Angielsku
Zadanie Domowe (10pkt)
Ściągnij kod:
Otwórz kod w środowisku IntelliJ IDEA.
Wybierz open project i wybierz pierwszy katalo nad src.
Wybierz View->Tools->Maven
KLiknij odśwież (dwie zielone strzałeczki a następnie zaznacz compile i wybierz Play (zielony trójkącik)).
Następnie w okienku project manadżera po lewej stronie znajdź katalog test a w nim plik TestNIF.java kliknij prawym na tym pliku i wybierz Run...
Uruchomią się testy, i wszystkie pokażą błędy. Twoim celem jest osiągnięcie stanu w którym wszystkie testy zakończą się poprawnie.
Aby testy wykonały się poprawnie musisz dopisać strumienie w plikach z katalogu src. W każdym z nich znajduje się metoda napisana w "starej" Javie i pusta metoda w której Twoim zadaniem jest dopisać strumień zachowujący się tak samo jak podana już metoda. Testy powiedzą Ci czy osiągnąłeś sukces.
Kod Java proszę przesłać przez stronę:
Termin wykonania zadania: Czwartek 16.06.2017 do godziny 24.00.
Wykorzystano materiały z:
http://java.amitph.com/2014/01/understanding-java-8-streams-api.html
https://github.com/vfarcic/java-8-exercises/
http://www.w3ii.com/pl/java8/java8_streams.html
http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
https://blog.idrsolutions.com/2014/11/java-8-streams-explained-5-minutes/
https://zeroturnaround.com/rebellabs/java-8-streams-cheat-sheet/
http://java.amitph.com/2014/02/java-8-streams-api-intermediate.html
http://java.amitph.com/2014/02/java-8-streams-api-terminal-operations.html