1. Збирання елементів
І нарешті ми дійшли до найцікавішого методу в класі Stream
— методу collect()
. Він використовується для того, щоб перейти від потоків до звичних колекцій — List<T>
, Set<T>
, Map<T, R>
та інших.
У метод collect()
потрібно передати спеціальний об'єкт — collector
. Цей об'єкт зчитує всі дані з потоку, перетворює їх на певну колекцію та повертає її. А слідом за ним цю саму колекцію повертає й сам метод collect.
Усе це влаштовано досить хитро: об'єкт collector
має тип Collector<T, A, R>
— у нього аж три типи-параметри. Останній тип R
зазвичай і є типом на кшталт List<T>
. Тому компілятор може за цим типом підставити правильний тип результату самого методу collect()
.
Сподіваємося, ви не сильно заплуталися. У будь-якому разі власноруч створювати об'єкти типу Collector вам не потрібно. Досить просто скористатися вже готовими об'єктами, що їх повертають статичні методи класу Collectors
.
Клас Collectors
Клас Collectors
має кілька статичних методів, які повертають готові об'єкти-колектори на всі випадки життя. Їх кілька десятків, але ми розглянемо основні:
|
Об'єкт, який перетворює потік на список List<T> |
|
Об'єкт, який перетворює потік на множину Set<T> |
|
Об'єкт, який перетворює потік на мапу Map<K, V> |
|
Склеює елементи потоку в один рядок |
|
Перетворює елементи потоку на Map<K, V> |
|
Групує елементи, повертає Map <K, V> |
2. Перетворення потоку на список
Отакий вигляд має типова робота з потоком і перетворення результату роботи на список:
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "Привіт", "як", "справи?");
List<String> result = list.stream()
.filter( s -> Character.isUpperCase(s.charAt(0)) )
.collect( Collectors.toList() );
Ми отримали потік із колекції, потім із нього отримали новий потік, відфільтрувавши тільки рядки, перший символ яких записано у верхньому регістрі. Відтак усі дані з останнього потоку зібрали в колекцію та повернули її.
3. Перетворення потоку на множину
Отакий вигляд має типова робота з потоком і перетворення результату роботи на множину:
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "Привіт", "як", "справи?");
Set<String> result = list.stream()
.filter( s -> Character.isUpperCase(s.charAt(0)) )
.collect( Collectors.toSet() );
Усе дуже схоже на код перетворення потоку на List
, тільки використовується інший об'єкт-колектор, що його повертає метод toSet();
4. Перетворення потоку на мапу
А от перетворити потік на мапу трохи складніше, адже кожен об'єкт Map складається з двох елементів — ключа і значення. Нам треба придумати, як в елемента потоку ми визначатимемо ключ, а як — значення.
Приклад.
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "a=2", "b=3", "c=4", "d==3");
Map<String, String> result = list.stream()
.map( e -> e.split("=") )
.filter( e -> e.length == 2 )
.collect( Collectors.toMap(e -> e[0], e -> e[1]) );
Спробуймо розібратися, що тут відбувається.
У першому рядку map(…)
ми перетворюємо кожен рядок на масив рядків. Використовуючи метод split, ми ділимо кожен рядок на дві частини. Роздільником є знак рівності.
У другому рядку — метод filter()
— ми пропускаємо крізь фільтр лише ті елементи-масиви, які містять рівно два елементи. Елемент d == 3
було розбито на масив із трьох елементів — він фільтр не пройде.
І насамкінець в останньому рядку ми перетворюємо потік на Map<String, String>
. У метод toMap()
передаються дві функції. Для кожного елемента потоку перша функція має повернути ключ, а друга — значення.
У нас ключем буде перший елемент масиву ("a", "b", "c"), а значенням — другий елемент масиву: "2", "3", "4".
5. Перетворення потоку на рядок
Ще один цікавий об'єкт-колектор — це Collectors.joining()
. Він перетворює всі елементи потоку на тип String
і склеює їх в один рядок. Приклад:
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "a=2", "b=3", "c=4", "d==3");
String result = list.stream().collect( Collectors.joining(", ") );
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ