Как stream преобразовать в list java
Большинство операций класса Stream, которые модифицируют набор данных, возвращают этот набор в виде потока. Однако бывают ситуации, когда хотелось бы получить данные не в виде потока, а в виде обычной коллекции, например, ArrayList или HashSet. И для этого у класса Stream определен метод collect . Первая версия метода принимает в качестве параметра функцию преобразования к коллекции:
R collect(Collector collector)
Параметр R представляет тип результата метода, параметр Т — тип элемента в потоке, а параметр А — тип промежуточных накапливаемых данных. В итоге параметр collector представляет функцию преобразования потока в коллекцию.
Эта функция представляет объект Collector, который определен в пакете java.util.stream. Мы можем написать свою реализацию функции, однако Java уже предоставляет ряд встроенных функций, определенных в классе Collectors :
- toList() : преобразование к типу List
- toSet() : преобразование к типу Set
- toMap() : преобразование к типу Map
Например, преобразуем набор в потоке в список:
import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; public class Program < public static void main(String[] args) < Listphones = new ArrayList(); Collections.addAll(phones, "iPhone 8", "HTC U12", "Huawei Nexus 6P", "Samsung Galaxy S9", "LG G6", "Xiaomi MI6", "ASUS Zenfone 2", "Sony Xperia Z5", "Meizu Pro 6", "Lenovo S850"); List filteredPhones = phones.stream() .filter(s->s.length() <10) .collect(Collectors.toList()); for(String s : filteredPhones)< System.out.println(s); >> >
Использование метода toSet() аналогично.
Set filteredPhones = phones.stream() .filter(s->s.length()<10) .collect(Collectors.toSet());
Для применения метода toMap() надо задать ключ и значение. Например, пусть у нас есть следующая модель:
class Phone < private String name; private int price; public Phone(String name, int price)< this.name=name; this.price=price; >public String getName() < return name; >public int getPrice() < return price; >>
Теперь применим метод toMap() :
import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; public class Program < public static void main(String[] args) < StreamphoneStream = Stream.of(new Phone("iPhone 8", 54000), new Phone("Nokia 9", 45000), new Phone("Samsung Galaxy S9", 40000), new Phone("LG G6", 32000)); Map phones = phoneStream .collect(Collectors.toMap(p->p.getName(), t->t.getPrice())); phones.forEach((k,v)->System.out.println(k + " " + v)); > > class Phone < private String name; private int price; public Phone(String name, int price)< this.name=name; this.price=price; >public String getName() < return name; >public int getPrice() < return price; >>
Лямбда-выражение p->p.getName() получает значение для ключа элемента, а t->t.getPrice() - извлекает значение элемента.
Если нам надо создать какой-то определенный тип коллекции, например, HashSet, то мы можем использовать специальные функции, которые определены в классах-коллекций. Например, получим объект HashSet:
import java.util.HashSet; import java.util.stream.Collectors; import java.util.stream.Stream; public class Program < public static void main(String[] args) < Streamphones = Stream.of("iPhone 8", "HTC U12", "Huawei Nexus 6P", "Samsung Galaxy S9", "LG G6", "Xiaomi MI6", "ASUS Zenfone 2", "Sony Xperia Z5", "Meizu Pro 6", "Lenovo S850"); HashSet filteredPhones = phones.filter(s->s.length()<12). collect(Collectors.toCollection(HashSet::new)); filteredPhones.forEach(s->System.out.println(s)); > >
Выражение HashSet::new представляет функцию создания коллекции. Аналогичным образом можно получать другие коллекции, например, ArrayList:
ArrayList result = phones.collect(Collectors.toCollection(ArrayList::new));
Вторая форма метода collect имеет три параметра:
R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)
- supplier : создает объект коллекции
- accumulator : добавляет элемент в коллекцию
- combiner : бинарная функция, которая объединяет два объекта
Применим эту версию метода collect:
import java.util.ArrayList; import java.util.stream.Collectors; import java.util.stream.Stream; public class Program < public static void main(String[] args) < Streamphones = Stream.of("iPhone 8", "HTC U12", "Huawei Nexus 6P", "Samsung Galaxy S9", "LG G6", "Xiaomi MI6", "ASUS Zenfone 2", "Sony Xperia Z5", "Meizu Pro 6", "Lenovo S850"); ArrayList filteredPhones = phones.filter(s->s.length()<12) .collect( ()->new ArrayList(), // создаем ArrayList (list, item)->list.add(item), // добавляем в список элемент (list1, list2)-> list1.addAll(list2)); // добавляем в список другой список filteredPhones.forEach(s->System.out.println(s)); > >
Convert a Stream to a List in Java
This post will discuss how to convert a stream to a list in Java.
1. Using Collectors class
We can collect elements of a stream in a container using the Stream.collect() method, which accepts a collector. We can pass collector returned by Collectors.toList() that accumulates the elements of the stream into a new list. This is the standard way to convert the stream to a list in Java 8 and above.
Stream
List
System . out . println ( list . toString ( ) ) ;
But Collectors.toList() doesn’t guarantee the type of the list returned. To have more control over the returned list, we can use the Collectors.toCollection() method to accept a constructor reference to the desired list.
Stream
List
System . out . println ( list . toString ( ) ) ;
2. Using Divide & Conquer
We can easily divide the problem into two parts:
- Convert stream to an array
- Convert array back to a list
The following program demonstrates it:
Как преобразовать IntStream в List?
Почему в первом случае при попытке преобразовать стрим в лист - ошибка компиляции, а во втором - все ок? По сути, ведь идентичные действия?
public class Test1 < public static void main(String[] args) < // Вариант 1 int[] array = ; List integerList = Arrays.stream(array) .filter(s -> s % 2 == 0) .collect(Collectors.toList()); // compile error // Вариант 2 List stringList = Arrays.stream(args) .filter(s -> s.length() >
Отслеживать
user419011
задан 11 окт 2020 в 5:32
29 1 1 серебряный знак 5 5 бронзовых знаков
Текст ошибки добавьте в вопрос.
11 окт 2020 в 5:39
Скорее всего потому, что int - премитивный тип, а Integer и String - нет
11 окт 2020 в 6:17
Текст ошибки: Expected 3 arguments bun found 1.
11 окт 2020 в 6:38
Если вы получили исчерпывающий ответ, отметьте его как принятый (плюсик и галочка слева от ответа)
– user419011
4 дек 2020 в 17:19
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Проблема заключается в том, что int - примитивный тип, поэтому в первом случае у вас используется не стандартный Stream , а IntStream , который является последовательностью примитивных целочисленных значений. Так как вы не можете создать список, хранящий примитивы, необходимо их превратить в их объектную оболочку (т.е int -> Integer ). Для этого используйте метод boxed() :
List integerList = Arrays.stream(array) .filter(s -> s % 2 == 0) .boxed() .collect(Collectors.toList());
Отслеживать
ответ дан 11 окт 2020 в 8:38
182 2 2 серебряных знака 15 15 бронзовых знаков
Метод Arrays.stream(int[]) возвращает IntStream , который затем можно преобразовать.
-
Получаем массив int[] с помощью метода toArray :
int[] array = ; int[] array2 = // получаем IntStream // по массиву int[] Arrays.stream(array) // отбираем четные числа .filter(s -> s % 2 == 0) // преобразовываем // в массив int[] .toArray(); System.out.println(Arrays.toString(array2)); // [2, 4, 6, 8, 10]
int[] array = ; List integerList = // получаем IntStream // по массиву int[] Arrays.stream(array) // отбираем четные числа .filter(s -> s % 2 == 0) // преобразовываем // в Stream .boxed() // собираем в лист .collect(Collectors.toList()); System.out.println(integerList); // [2, 4, 6, 8, 10]
Java Stream API. Копилка рецептов
Если вы не любите стримы, возможно, вы пока не умеете их готовить 🙂 Приглашаем поучиться.


pavel starikov / flickr

Екатерина Степанова
Фулстек-разработчик. Любимый стек: Java + Angular, но в хорошей компании готова писать хоть на языке Ада.
В этой статье почти нет теории, зато много практики и кода. Разберём семь типичных ситуаций, когда стримы бывают полезны. Сравним решения с классическими императивными реализациями.
Stream API — что это вообще такое
Это способ работать со структурами данных Java, чаще всего коллекциями, в стиле функциональных языков программирования.
О началах функционального программирования и лямбдах в Java читайте здесь.
Стрим — это объект для универсальной работы с данными. И это вовсе не какая-то новая структура данных, он использует существующие коллекции для получения новых элементов.
Затем к данным применяются методы. В интерфейсе Stream их множество. Каждый выполняет одну из типичных операций с коллекцией: отсортировать, перегруппировать, отфильтровать. Мы разберём некоторые из этих методов дальше.
Думайте о стриме как о потоке данных, а о цепочке вызовов методов — как о конвейере.
Каждый промежуточный метод получает на вход результат выполнения с предыдущего этапа (стрим), отвечает только за свою часть работы и возвращает стрим.
Последний (терминальный) метод либо не возвращает значения (void), либо возвращает результат иного, нежели стрим, типа.
Преимущества
Стримы избавляют программистов от написания стереотипного кода всякий раз, когда нужно сделать что-то с набором элементов. То есть благодаря стримам не приходится думать о деталях реализации.
Есть и другие плюсы:
- Стримы поддерживают один из основных принципов хорошего проектирования — слабую связанность (low coupling). Чем меньше класс знает про другие классы — тем лучше. Алгоритму сортировки не должно быть важно, что конкретно он сортирует. Это и делают стримы.
- С помощью стримов операции с коллекциями проще распараллелить: в императивном подходе для этого бы понадобился минимум ещё один цикл.
- Стримы позволяют уменьшить число побочных эффектов: методы Stream API не меняют исходные коллекции.
- Со Stream API лаконично записываются сложные алгоритмы обработки данных.
А теперь, когда вы почти поверили, что стримы — это хорошо, перейдём к практике.
Подготовим данные
Работу методов Java Stream API покажем на примере офлайновой библиотеки. Для каждой книги библиотечного фонда известны автор, название и год издания.
Для читателя библиотеки будем хранить ФИО и электронный адрес. Каждый читатель может взять в библиотеке одну или несколько книг — их тоже сохраним.
Ещё нам понадобится флаг читательского согласия на уведомления по электронной почте. Рассылки организуют сотрудники библиотеки: напоминают о сроке возврата книг, сообщают новости.
Вот как это выглядит на Java:

А далее о случае, когда эта операция бывает полезной.
Задача
Получить список всех книг, взятых читателями. Список не должен содержать дубликатов (книг одного автора, с одинаковым названием и годом издания).
Без лямбд

Задача
Узнать наибольшее число книг, которое сейчас на руках у читателя.
Без лямбд
int max = 0; for (Reader reader : library.getReaders())< if (reader.getBooks().size() > max) max = reader.getBooks().size(); > return max;
На старте принимаем за максимум наименьшее возможное число книг у каждого читателя, то есть 0. Потом перебираем всех читателей, смотрим, сколько у кого книг, и при необходимости обновляем максимум.
С лямбдами
Integer reduce = library.getReaders().stream() .map(reader -> reader.getBooks().size()) .reduce(0, (max, size) -> size > max ? size : max);
На первом шаге с помощью map() соотносим с каждым читателем число взятых им книг, а затем с помощью reduce() находим максимальный элемент в этом новом стриме.
Это важно. При каждом вызове функции-аккумулятора создаётся новый объект. Если вы хотите, чтобы на выходе reduce() оказался сложный объект — например, коллекция, и ходите в функции-аккумуляторе добавлять в неё значения, то на каждом шаге будет создаваться новая коллекция.
Это плохо сказывается на производительности. В таких случаях лучше использовать collect().
Для группировки — collect() + Collectors.groupingBy() и Collectors.mapping()
Методы groupingBy() и mapping() вовсе не обязательно применять вместе. Первый позволяет разбить стрим на группы по заданному признаку. Если эти группы нужны в виде списков, то второй метод не понадобится.
mapping() выручит, если полученные группы нужно хитрым (или не очень) образом преобразовать (например, сгруппировать по другим признакам).
Задача
Вернёмся к нашим баранам email-рассылкам. Теперь нужно не просто отправить письма всем, кто согласился на рассылку, — будем рассылать разные тексты двум группам:
- тем, у кого взято меньше двух книг, просто расскажем о новинках библиотеки;
- тем, у кого две книги и больше, напомним о том, что их нужно вернуть в срок.
То есть надо написать метод, который вернёт два списка адресов (типа EmailAddress): с пометкой OK — если книг не больше двух, или TOO_MUCH — если их две и больше. Порядок групп не важен.
Без лямбд
Приготовьтесь, сейчас будет страшно.
Map> result = new HashMap<>(); for (Reader reader : library.getReaders()) < if (reader.isSubscriber()) < if (reader.getBooks().size() > 2) < if (!result.containsKey("TOO_MUCH")) < result.put("TOO_MUCH", new ArrayList<>()); > result.get("TOO_MUCH").add(new EmailAddress(reader.getEmail())); > else < if (!result.containsKey("OK")) < result.put("OK", new ArrayList<>()); > result.get("OK").add(new EmailAddress(reader.getEmail())); > > > return result;
Цикл и три уровня ветвлений. И это всего для двух групп!
С лямбдами
Map> map = library.getReaders().stream() .filter(Reader::isSubscriber) .collect(groupingBy(r -> r.getBooks().size() > 2 ? "TOO_MUCH" : "OK", mapping(r -> new EmailAddress(r.getEmail()), Collectors.toList())));
На первом шаге фильтруем читателей: оставляем только тех, кто согласился на рассылку. Дальше настраиваем параметры метода collect():
- задаём группировку — нужно разбить стрим на две группы по числу книг: «TOO_MUCH» или «OK»;
- в каждой группе берём email-адреса читателей (new EmailAddress (r.getEmail())) и собираем их в списки (Collectors.toList()).
Вариации на тему
1. Если нужны не адреса, а просто списки читателей в каждой группе:
MapString, List> readerstMap = library.getReaders().stream() .filter(Reader::isSubscriber) .collect(groupingBy(r -> r.getBooks().size() > 2 ? "TOO_MUCH" : "OK"));
2. Если для каждой группы нужны ФИО читателей из этой группы, перечисленные через запятую. И ещё каждый такой список ФИО нужно обернуть фигурными скобками.
Например:
Map readersFIOMap = library.getReaders().stream() .filter(Reader::isSubscriber) .collect(groupingBy(r -> r.getBooks().size() > 2 ? "TOO_MUCH" : "OK", mapping(Reader::getFio, joining(", ", ", ">"))));
Что дальше?
Ещё больше о Stream API вы узнаете из официальной документации и на нашем курсе «Профессия Java-разработчик».
Это когда программа записывается как последовательное применение функций к некоторым значениям и другим функциям, а не как сложная структура из циклов, условных операторов и перекладывания значений туда-сюда.
Это функциональный интерфейс, который принимает один параметр и возвращает значение типа boolean.