Введение в Java 8 Streams

Введение в потоки 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.