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 есть несколько статических методов, которые возвращают готовые объекты-коллекторы на все случаи жизни. Их несколько десятков, но мы рассмотрим самые основные:

toList()
Объект, который преобразует поток в список — List<T>
toSet()
Объект, который преобразует поток во множество — Set<T>
toMap()
Объект, который преобразует поток в мэп — Map<K, V>
joining()
Склеивает элементы потока в одну строку
mapping()
Преобразует элементы потока в Map<K, V>
groupingBy()
Группирует элементы, возвращает 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(", ") );