JavaRush /Курсы /Java Syntax Pro /Коллекторы в Java

Коллекторы в Java

Java Syntax Pro
18 уровень , 6 лекция
Открыта

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(", ") );


Комментарии (190)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
LisoEdoEd Уровень 20 Student
16 декабря 2025
Без сторонних источников, конечно, не разобрался. Но не плохо...
Anonymous #3585174 Уровень 33
24 июля 2025
Like
Agent_Pandora Уровень 26
14 апреля 2025
Тема пушка
Cryptosin Уровень 24
31 марта 2025

return stringStream
    .collect(Collectors.toMap(s -> s, String::length, (oldValue, newValue) -> newValue));
используем

(oldValue, newValue) -> newValue)
иначе код выбросит ошибку IllegalStateException, потому что по умолчанию toMap() не умеет автоматически разрешать конфликты ключей. Это в задаче не надо, но возможно будет полезно узнать, тк мне стало интересно, что происходит, если будут одинаковые ключи 🙂
S(Anonymous #3313184) Уровень 1
14 марта 2025
В лекции .collect(Collectors.toList()), в примере правильное решение .collect(toList()) в чем прикол?
Иван Сахаров Уровень 32
24 марта 2025
Потому что там уже импортировали import static java.util.stream.Collectors.toList;
Юрий Уровень 20
1 марта 2025
История всё больше напоминает про вархаммер 40к
Rustam Уровень 23
9 февраля 2025
Anonymous #3525583 Уровень 28
29 января 2025
Почему в лекции даются неправильные примеры, что это вообще такое?
Ilya Klimchev Уровень 1
20 февраля 2025
какой пример неправильный?
Faraway_is_close Уровень 24
18 января 2025
4. Преобразование потока в мэп Нужно переделывать раздел. Пример в лекции только сбивает с толку. Решал задачу 45 минут, и так и не решил без GPT.
марк Уровень 25
4 марта 2025
решил за 2 минуты, если вас пример сбивает с толку, значит вы не понимает, что происходит в коде, проблема не в примере.
Anonymous #3554585 Уровень 68
12 апреля 2025
А что не так с примером? Помойму очевидно, что 1-й параметр ключ, 2-й значение.
Polly Уровень 32
16 января 2025
Ребята, подскажите по стилю, пожалуйста. Из правильных решений не совсем ясно, какой вариант написания считается "красивым", если к стриму мы добавляем только одно звено цепи: return stringStream.filter( e -> e.length == 2 ); или return stringStream .filter( e -> e.length == 2 );
Ilya Klimchev Уровень 1
20 февраля 2025
Дело не в количестве звеньев) Если от левого края дисплея до ";" задействовано не более 3/4 ширины экрана, то неважно, сколько звеньев в одной строке...Если выезжает за ширину дисплея и звеньев > 2, то лично мне кажется, что каждое звено лучше с новой строки начинать. Ещё раз: все эти стримы нужны только для того, чтобы писать меньше кода. Но, если в результате этот код невозможно читать, то какой смысл?)