Перейти к основному содержимому

Создание потока данных

Для создания потока данных можно применять различные методы. В качестве источника потока мы можем использовать коллекции. В частности, в JDK 8 в интерфейс Collection, который реализуется всеми классами коллекций, были добавлены два метода для работы с потоками:

  • default Stream<E> stream: возвращается поток данных из коллекции.
  • default Stream<E> parallelStream: возвращается параллельный поток данных из коллекции.

Так, рассмотрим пример с ArrayList:

import java.util.stream.Stream;
import java.util.*;

public class Program {
public static void main(String[] args) {
ArrayList<String> cities = new ArrayList<>();
Collections.addAll(cities, "Париж", "Лондон", "Мадрид");
cities.stream() // получаем поток
.filter(s -> s.length() == 6) // применяем фильтрацию по длине строки
.forEach(s -> System.out.println(s)); // выводим отфильтрованные строки в консоль
}
}

Здесь с помощью вызова cities.stream() получаем поток, который использует данные из списка cities. С помощью каждой промежуточной операции, которая применяется к потоку, мы также можем получить поток с учетом модификаций. Например, мы можем изменить предыдущий пример следующим образом:

ArrayList<String> cities = new ArrayList<>();
Collections.addAll(cities, "Париж", "Лондон", "Мадрид");

Stream<String> citiesStream = cities.stream(); // получаем поток
citiesStream = citiesStream.filter(s -> s.length() == 6); // применяем фильтрацию по длине строки
citiesStream.forEach(s -> System.out.println(s)); // выводим отфильтрованные строки в консоль

Важно, что после использования терминальных операций другие терминальные или промежуточные операции к этому же потоку не могут быть применены, поток уже употреблен. Например, в следующем случае мы получим ошибку:

citiesStream.forEach(s -> System.out.println(s)); // терминальная операция употребляет поток
long number = citiesStream.count(); // здесь ошибка, так как поток уже употреблен
System.out.println(number);
citiesStream = citiesStream.filter(s -> s.length() > 5); // тоже нельзя, так как поток уже употреблен

Фактически жизненный цикл потока проходит следующие три стадии:

  1. Создание потока.
  2. Применение к потоку ряда промежуточных операций.
  3. Применение к потоку терминальной операции и получение результата.

Кроме рассмотренных выше методов мы можем использовать ещё ряд способов для создания потока данных. Один из таких способов представляет метод Arrays.stream(T[] array), который создаёт поток данных из массива:

Stream<String> citiesStream = Arrays.stream(new String[] {"Париж", "Лондон", "Мадрид"});
citiesStream.forEach(s -> System.out.println(s)); // выводим все элементы массива

Для создания потоков IntStream, DoubleStream, LongStream можно использовать соответствующие перегруженные версии этого метода:

IntStream intStream = Arrays.stream(new int[] {1, 2, 4, 5, 7});
intStream.forEach(i -> System.out.println(i));

LongStream longStream = Arrays.stream(new long[] {100, 250, 400, 5843787, 237});
longStream.forEach(l -> System.out.println(l));

DoubleStream doubleStream = Arrays.stream(new double[] {3.4, 6.7, 9.5, 8.2345, 121});
doubleStream.forEach(d -> System.out.println(d));

И ещё один способ создания потока представляет статический метод of(T..values) класса Stream:

Stream<String> citiesStream = Stream.of("Париж", "Лондон", "Мадрид");
citiesStream.forEach(s -> System.out.println(s));

// можно передать массив
String[] cities = {"Париж", "Лондон", "Мадрид"};
Stream<String> citiesStream2 = Stream.of(cities);

IntStream intStream = IntStream.of(1, 2, 4, 5, 7);
intStream.forEach(i -> System.out.println(i));

LongStream longStream = LongStream.of(100, 250, 400, 5843787, 237);
longStream.forEach(l -> System.out.println(l));

DoubleStream doubleStream = DoubleStream.of(3.4, 6.7, 9.5, 8.2345, 121);
doubleStream.forEach(d -> System.out.println(d));