Большинство операций класса Stream, которые модифицируют набор данных, возвращают этот набор в виде потока. Однако бывают ситуации, когда хотелось бы получить данные не в виде потока, а в виде обычной коллекции, например, ArrayList или HashSet. И для этого у класса Stream определен метод collect. Первая версия метода принимает в качестве параметра функцию преобразования к коллекции:
<R,A> R collect(Collector<? super T,A,R> 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) { List<String> phones = new ArrayList<String>(); 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<String> filteredPhones = phones.stream() .filter(s->s.length()<10) .collect(Collectors.toList()); for(String s : filteredPhones){ System.out.println(s); } } }
Использование метода toSet()
аналогично.
Set<String> 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) { Stream<Phone> phoneStream = Stream.of(new Phone("iPhone 8", 54000), new Phone("Nokia 9", 45000), new Phone("Samsung Galaxy S9", 40000), new Phone("LG G6", 32000)); Map<String, Integer> 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) { Stream<String> phones = 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<String> filteredPhones = phones.filter(s->s.length()<12). collect(Collectors.toCollection(HashSet::new)); filteredPhones.forEach(s->System.out.println(s)); } }
Выражение HashSet::new
представляет функцию создания коллекции. Аналогичным образом можно получать другие коллекции, например, ArrayList:
ArrayList<String> result = phones.collect(Collectors.toCollection(ArrayList::new));
Вторая форма метода collect имеет три параметра:
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> 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) { Stream<String> phones = 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<String> filteredPhones = phones.filter(s->s.length()<12) .collect( ()->new ArrayList<String>(), // создаем ArrayList (list, item)->list.add(item), // добавляем в список элемент (list1, list2)-> list1.addAll(list2)); // добавляем в список другой список filteredPhones.forEach(s->System.out.println(s)); } }