Введение в потоки Java 8
1. обзор
В этой статье мы кратко рассмотрим одну из основных частей новой функциональности, которую добавила Java 8, - потоки.
Мы объясним, что такое потоки, и продемонстрируем создание и основные операции с потоками на простых примерах.
2. Stream API
Одной из основных новых функций в Java 8 является введение функции потока -java.util.stream - которая содержит классы для обработки последовательностей элементов.
Центральный API-класс -Stream<T>.. В следующем разделе будет показано, как можно создавать потоки с использованием существующих источников поставщиков данных.
2.1. Создание потока
Потоки могут быть созданы из разных источников элементов, например сбор или массив с помощью методовstream() иof():
String[] arr = new String[]{"a", "b", "c"};
Stream stream = Arrays.stream(arr);
stream = Stream.of("a", "b", "c");
Метод по умолчаниюstream() добавлен к интерфейсуCollection и позволяет создаватьStream<T>, используя любую коллекцию в качестве источника элемента:
Stream stream = list.stream();
2.2. Многопоточность с потоками
Stream API также упрощает многопоточность, предоставляя методparallelStream(), который выполняет операции над элементами потока в параллельном режиме.
Приведенный ниже код позволяет запускать методdoWork() параллельно для каждого элемента потока:
list.parallelStream().forEach(element -> doWork(element));
В следующем разделе мы познакомимся с некоторыми основными операциями Stream API.
3. Потоковые операции
Есть много полезных операций, которые могут быть выполнены в потоке.
Они делятся наintermediate operations (возвратStream<T>) иterminal operations (возврат результата определенного типа). Промежуточные операции позволяют создавать цепочки.
Также стоит отметить, что операции с потоками не меняют источник.
Вот быстрый пример:
long count = list.stream().distinct().count();
Итак, методdistinct() представляет собой промежуточную операцию, которая создает новый поток уникальных элементов предыдущего потока. А методcount() - это терминальная операция,, которая возвращает размер потока.
3.1. Итерация
Stream API помогает заменить циклыfor, for-each иwhile. Это позволяет сосредоточиться на логике работы, а не на итерации по последовательности элементов. Например:
for (String string : list) {
if (string.contains("a")) {
return true;
}
}
Этот код можно изменить только одной строкой кода Java 8:
boolean isExist = list.stream().anyMatch(element -> element.contains("a"));
3.2. фильтрация
Методfilter() позволяет нам выбирать поток элементов, удовлетворяющих предикату.
Например, рассмотрим следующий список:
ArrayList list = new ArrayList<>();
list.add("One");
list.add("OneAndOnly");
list.add("Derek");
list.add("Change");
list.add("factory");
list.add("justBefore");
list.add("Italy");
list.add("Italy");
list.add("Thursday");
list.add("");
list.add("");
Следующий код создаетStream<String> изList<String>, находит все элементы этого потока, которые содержатchar “d”, и создает новый поток, содержащий только отфильтрованные элементы:
Stream stream = list.stream().filter(element -> element.contains("d"));
3.3. картографирование
Чтобы преобразовать элементыStream, применив к ним специальную функцию, и собрать эти новые элементы вStream, мы можем использовать методmap():
List uris = new ArrayList<>();
uris.add("C:\\My.txt");
Stream stream = uris.stream().map(uri -> Paths.get(uri));
Итак, приведенный выше код преобразуетStream<String> вStream<Path> путем применения определенного лямбда-выражения к каждому элементу начальногоStream.
Если у вас есть поток, в котором каждый элемент содержит свою собственную последовательность элементов, и вы хотите создать поток из этих внутренних элементов, вам следует использовать методflatMap():
List details = new ArrayList<>();
details.add(new Detail());
Stream stream
= details.stream().flatMap(detail -> detail.getParts().stream());
В этом примере у нас есть список элементов типаDetail.. КлассDetail содержит полеPARTS,, которое являетсяList<String>.. С помощьюflatMap() Каждый элемент из поляPARTS будет извлечен и добавлен в новый результирующий поток. После этого начальныйStream<Detail> будет потерян.
3.4. согласование
Stream API предоставляет удобный набор инструментов для проверки элементов последовательности в соответствии с некоторым предикатом. Для этого можно использовать один из следующих методов:anyMatch(), allMatch(), noneMatch(). Их имена говорят сами за себя. Это терминальные операции, которые возвращают логическое значение.
boolean isValid = list.stream().anyMatch(element -> element.contains("h")); // true
boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); // false
boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h")); // false
3.5. снижение
Stream API позволяет уменьшить последовательность элементов до некоторого значения в соответствии с заданной функцией с помощью методаreduce() типаStream. Этот метод принимает два параметра: первый - начальное значение, второй - функция аккумулятора.
Представьте, что у вас естьList<Integer>, и вы хотите получить сумму всех этих элементов и некоторых начальныхInteger (в этом примере 23). Таким образом, вы можете запустить следующий код и результат будет 26 (23 + 1 + 1 + 1).
List integers = Arrays.asList(1, 1, 1);
Integer reduced = integers.stream().reduce(23, (a, b) -> a + b);
3.6. сбор
Уменьшение также может быть обеспечено методомcollect() типаStream.. Эта операция очень удобна в случае преобразования потока вCollection илиMap и представления потока в виде одной строки. Существует служебный классCollectors, который обеспечивает решение почти для всех типичных операций сбора. Для некоторых нетривиальных задач можно создать собственныйCollector.
List resultList
= list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());
Этот код использует операцию терминалаcollect() для уменьшенияStream<String> доList<String>.
4. Выводы
В этой статье мы кратко затронули потоки Java - определенно одну из самых интересных функций Java 8.
Есть гораздо более продвинутые примеры использования потоков; цель этой статьи состояла в том, чтобы дать быстрое и практическое введение в то, что вы можете начать делать с функциональностью, и как отправную точку для изучения и дальнейшего изучения.
Доступен исходный код к статьеover on GitHub.