Що таке Stream API?
Stream API – це новий спосіб працювати зі структурами даних у функціональному стилі. Stream (потік) API (опис способів, якими одна комп'ютерна програма може взаємодіяти з іншою програмою) - це по суті потік даних. Сам термін "потік" досить розмитий у програмуванні загалом і Java зокрема.List<String> list = new ArrayList<String>();
list.add("One");
list.add("Two");
list.add("Three");
list.add("Four");
list.add("Five");
list.add("Six");
list.add("Seven");
list.add("Eight");
list.add("Nine");
list.add("Ten");
Stream stream = list.stream();
Як говорилося вище, Stream API дозволяє скоротити кількість рядків коду. Приклад з потоком:
IntStream.of(50, 60, 70, 80, 90, 100, 110, 120).filter(x -> x < 90).map(x -> x + 10)
.limit(3).forEach(System.out::print);
Приклад без потоку:
int[] arr = {50, 60, 70, 80, 90, 100, 110, 120
int count = 0;
for (int x : arr) {
if (x >= 90) continue;
x += 10;
count++;
if (count > 3) break;
System.out.print(x);
}
Можливі способи створення Stream:
- Порожній стрим:
Stream.empty()
- Стрім із List:
list.stream()
- Стрім із Map:
map.entrySet().stream()
- Стрім із масиву:
Arrays.stream(array)
- Стрім із зазначених елементів:
Stream.of("1", "2", "3")
- Проміжні (“intermediate”, ще називають “lazy”) – обробляють елементи, що надходять, і повертають стрим. Проміжних операторів у ланцюжку обробки елементів може бути багато.
- Термінальні (“terminal”, ще називають “eager”) – обробляють елементи і завершують роботу стриму, отже термінальний оператор у ланцюжку може лише один.
1.List<String> list = new ArrayList<String>();
2.list.add("One");
…
11.list.add("Ten");
12.Stream stream = list.stream();
13.stream.filter(x-> x.toString().length() == 3).forEach(System.out::println);
Що тут відбувається:
- 1 - створюємо список
list
; - 2-11 - заповнюємо його тестовими даними;
- 12 - створюємо об'єкт
Stream
; - 13 — метод
filter
(фільтр) — проміжний оператор,x
що прирівнюється до одного елементу колекції для перебору (як приfor each
) і після -> ми вказуємо як фільтрується наша колекція і так як це проміжний оператор, відфільтрована колекція йде далі в метод,forEach
який у свою чергу є термінальним (Кінцевим) аналогом переборуfor each
(ВиразSystem.out::println
скорочено від:x-> System.out.println(x))
, яке у свою чергу проходить по всіх елементах переданої йому колекції і виводить її)
- Обробка не почнеться доти, доки не буде викликано термінального оператора.
list.stream().filter(s -> s > 5)
(Не візьме жодного елемента зі списку); - Примірник, стрим не можна використовувати більше одного разу =( ;
Тому щоразу новий:
list.stream().filter(x-> x.toString().length() == 3).forEach(System.out::println);
list.stream().forEach(x -> System.out.println(x));
проміжних операторів викликаних одному стримі може бути безліч, тоді термінальний оператор лише один:
stream.filter(x-> x.toString().length() == 3).map(x -> x + " - the length of the letters is three").forEach(x -> System.out.println(x));
filter(Predicate predicate)
фільтрує стрім, пропускаючи лише ті елементи, що проходять за умовою (Predicate вбудований функціональний інтерфейс, доданий у Java SE 8 в пакетjava.util.function
. Перевіряє значення на “ true ” та “ false ”);map(Function mapper)
дає можливість створити функцію за допомогою якої ми змінюватимемо кожен елемент і пропускатимемо його далі (Функціональний інтерфейсFunction<T,R>
представляє функцію переходу від об'єкта типу T до об'єкта типу R)flatMap(Function<T, Stream<R>> mapper)
- Як і у випадку зmap
, служать для перетворення на примітивний стрим.
[stream1,stream2,stream3,stream4] => stream
:
String[] array = {"Java", "Ruuuuussshhh"};
Stream<String> streamOfArray = Arrays.stream(array);
streamOfArray.map(s->s.split("")) //Перетворення слова в масив букв
.flatMap(Arrays::stream).distinct() //Вирівнює кожен згенерований потік в один потік
.collect(Collectors.toList()).forEach(System.out::println);
У той час коли map
перетворює на список потоків (точніше <Stream>
потоків) [stream1,stream2,stream3,stream4] =>Stream.of(stream1,stream2,stream3,stream4)
:
String[] array = {"Java", "Ruuuuussshhh"};
Stream<String> streamOfArray = Arrays.stream(array);
streamOfArray.map(s->s.split("")) //Перетворення слова в масив букв
.map(Arrays::stream).distinct() //Зробити масив в окремий потік
.collect(Collectors.toList()).forEach(System.out::println);
Ще одна відмінність у порівнянні з map
, можна перетворити один елемент на нуль, один або безліч інших. Для того, щоб один елемент перетворити на нуль елементів, потрібно повернути null
або порожній стрим. Щоб перетворити один елемент, потрібно повернути стрим з одного елемента, наприклад, через Stream.of(x)
. Для повернення кількох елементів можна будь-якими способами створити стрим з цими елементами. Той самий метод flatMap, але для Double, Integer та Long:
- flatMapToDouble(Function mapper)
- flatMapToInt(Function mapper)
- flatMapToLong(Function mapper)
Stream.of(2, 3, 0, 1, 3)
.flatMapToInt(x -> IntStream.range(0, x))
.forEach(System.out::print);// 010120012
-
IntStream.range(0,x) – видає на потік елементів з 0 (включно) до x (не включно);
map:
Stream.of(2, 3, 0, 1, 3) .map(x -> IntStream.range(0, x)) .forEach(System.out::print);//перелік стримувань (потоків);
-
limit(long maxSize) – обмежує стрим за кількістю елементів:
stream.limit(5).forEach(x -> System.out.println(x));
-
skip(long n) – пропускаємо n елементів:
stream.skip(3).forEach(x -> System.out.println(x));
-
sorted()
- sorted(Comparator comparator) – сортує стрим (сортування як у TreeMap):
stream.sorted().forEach(x -> System.out.println(x));
-
distinct() - перевіряє стриму на унікальність елементів (прибирає повтори елементів);
-
dropWhile(Predicate predicate) — пропускає елементи, які задовольняють умові (з'явився в 9 java, Функціональний інтерфейс Predicate<T> перевіряє дотримання певної умови. Якщо воно дотримується, то повертається значення true. В якості параметра лямбда-вираз приймає об'єкт типу T:
Predicate<Integer> isPositive = x -> x > 0; System.out.println(isPositive.test(3)); // true System.out.println(isPositive.test(-9)); // false
-
forEach(Consumer action) – аналог for each (Consumer<T> виконує деяку дію над об'єктом типу T, причому нічого не повертаючи);
-
count() – повертає кількість елементів стриму:
System.out.println(stream.count());
-
collect(Collector collector) – метод збирає всі елементи до списку, безліч чи іншу колекцію, згруповує елементи за якимось критерієм, об'єднує все в рядок і т.д.:
List<String> list = Stream.of(“One”, “Two”, “Three”).collect(Collectors.toList());
-
collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)
- Той же, що іcollect(collector)
тільки параметри розбиті для зручності (supplier
поставляє нові об'єкти (контейнери), наприкладnew ArrayList()
,accumulator
додає елемент в контейнер,combiner
об'єднує частини стриму воєдино); -
reduce(T identity, BinaryOperator accumulator) - перетворює всі елементи стриму в один об'єкт(порахувати суму всіх елементів, або знайти мінімальний елемент), спочатку береться об'єкт
identity
і перший елемент стриму, застосовується функціяaccumulator
іidentity
стає її результатом. Потім все продовжується для інших елементів.int sum = Stream.of(1, 2, 3, 4, 5).reduce(10, (acc, x) -> acc + x);// = 25
-
reduce(BinaryOperator accumulator)
- такий самий метод як і вище але відсутній початковийidentity
, їм служить перший елемент стримуOptional min(Comparator comparator)
Optional max(Comparator comparator) шукає мінімальний/максимальний елемент, ґрунтуючись на переданому компараторі; -
findFirst()
- Витягує перший елемент стриму:Stream.of(1, 2, 3, 4, 9).findFirst();
-
allMatch(Predicate predicate)
- Повертає true , якщо всі елементи стриму задовольняють умові. Якщо зустрічається якийсь елемент, для якого результат виклику функції-предикату буде false , то оператор перестає переглядати елементи і повертає false :Stream.of(1, 2, 3, 4, 9).allMatch(x -> x <= 7);//false
-
anyMatch(Predicate predicate)
— поверне true , якщо хоча б один елемент стриму задовольняє умовуpredicate
:Stream.of(1, 2, 3, 4, 9).anyMatch(x -> x >= 7);//true
-
noneMatch(Predicate predicate)
— поверне true , якщо, пройшовши всі елементи стриму, жоден не задовольнив умовуpredicate
:Stream.of(1, 2, 3, 4, 9).noneMatch(x -> x >= 7);//false
Collectors
:
-
toList()
- Збирає елементи вList
:List<Integer> list = Stream.of(99, 2, 3).collect(Collectors.toList());
-
toSet()
- Збирає елементи в безліч:Set<Integer> set = Stream.of(99, 2, 3).collect(Collectors.toSet());
-
counting()
- Підраховує кількість елементів:Long count = Stream.of("1", "2", "3", "4").collect(Collectors.counting());
-
joining()
-
joining(CharSequence delimiter)
-
joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
- Збирає елементи в один рядок. Додатково можна вказати роздільник, а також префікс та суфікс для всієї послідовності:String a = Stream.of("s", "u" ,"p", "e", "r").collect(Collectors.joining()); System.out.println(a); // super String b = Stream.of("s", "u", "p", "e", "r").collect(Collectors.joining("-")); System.out.println(b); // s-u-p-e-r String c = Stream.of("s", "u", "p", "e", "r").collect(Collectors.joining(" -> ", "[ ", " ]")); System.out.println(c); // [ s -> u -> p -> e -> r ]
-
summingInt(ToIntFunction mapper)
-
summingLong(ToLongFunction mapper)
-
summingDouble(ToDoubleFunction mapper)
- Колектор, який перетворює об'єкти в int/long/double та підраховує суму.