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(", ") );
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ