Java. Понятие конечной и промежуточной операции

Понятие конечной и промежуточной операции. Отличия. Примеры. Методы создания потока данных stream(), parallelStream()


Содержание


Поиск на других ресурсах:




1. Конечная и промежуточная операции. Определение

Под операцией подразумевается вызов метода, обрабатывающего поток. При обработке потока возникают понятия конечной операции и промежуточной операции.

Если в определении метода описывается термин «конечная операция«, то это значит что пересмотрен (обработан) весь поток от начала до конца и получен конечный результат. После этого данный поток считается потребленным и его повторно использовать нельзя. Если попробовать обработать потребленный поток после выполнения конечной операции (метода), то система сгенерирует исключительную ситуацию.

Если в описании операции (метода) указано «промежуточная операция«, то это означает, что создается (производится) новый поток данных, который может обрабатываться в дальнейшем в конвейерном режиме. Конвейерный режим предусматривает дальнейшее использование промежуточных или конечных операций над новосозданным потоком. В этом случае конечная операция вызывается последней.

 

2. Конечная операция. Пример

В примере демонстрируется использование конечных операций.

Условие задачи. Вычислить минимальное и максимальное значения в наборе случайных чисел типа Double.

Текст демонстрационной программы следующий.

import java.util.ArrayList;
import java.util.Optional;
import java.util.stream.*;

public class StreamAPI {

  public static void main(String[] args) {
    // Конвертировать в массив Double[] поток чисел
    // 1. Объявить набор чисел в виде коллекции
    ArrayList<Double> AL = new ArrayList<Double>();

    // 2. Заполнить набор чисел случайными значениями
    for (int i=0; i<10; i++)
      AL.add((Math.random()*10));

    // 3. Создать поток чисел
    Stream<Double> stream = AL.stream();

    // 4. Найти максимальное значение в массиве
    Optional<Double> max = stream.max(Double::compare);
    System.out.println("max = " + max.get());

    // 5. Попытка вызова еще одной операции - поиск минимума
    stream = AL.stream(); // Обязательно нужно создать новый поток
    Optional<Double> min = stream.min(Double::compare);
    System.out.println("min = " + min.get());
  }
}

В вышеприведенном коде сначала выполняется поиск максимума, затем выполняется поиск минимума. Операция поиска максимума

stream.max(Double::compare);

есть конечной. Это означает, что после ее выполнения поток считается потребленным. Потребленный поток невозможно дальше обрабатывать. Если после поиска максимума убрать строку

stream = AL.stream();

то после запуска на выполнение программа сгенерирует исключение IllegalStateException.

Результат выполнения программы

max = 8.627680677435304
min = 1.4456409633880085

 

3. Промежуточная операция. Пример

В примере демонстрируется выполнение промежуточной операции, которая сортирует поток чисел в порядке убывания.

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class StreamAPI {

  public static void main(String[] args) {
    // Сортировка потока чисел в порядке убывания значений
    // 1. Создать набор чисел
    ArrayList<Integer> AL = new ArrayList<Integer>();
    for (int i=0; i<10; i++)
      AL.add((int)(Math.random()*10));
    System.out.println("AL = " + AL);

    // 2. Создать поток чисел на основе коллекции AL
    Stream<Integer> stream = AL.stream();

    // 3. Вызвать промежуточную операцию сортировки - создается новый поток
    // 3.1. Создать метод сравнения
    Comparator<Integer> comparator = (a, b) -> b-a;

    // 3.2. Создать новый отсортированный поток - это промежуточная операция,
    //     после нее поток можно обрабатывать дальше
    Stream<Integer> sortStream = stream.sorted(comparator);

    // 3.3. Вывести новый поток
    System.out.print("sorted AL = ");

    // 3.3.1. Создать действие, которое выводит элемент потока на экран
    Consumer<Integer> action;

    action = (n) -> {
      System.out.print(n + " ");
    };

    // Передать действие action в метод forEach() для поэлементной обработки
    sortStream.forEach(action); // forEach() - конечная операция
  }
}

В вышеприведенном коде создается поток чисел stream из коллекции AL. Полученный поток сортируется промежуточной операцией sorted(). Метод sorted() образует новый отсортированный поток

Stream<Integer> sortStream = stream.sorted(comparator);

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

sortStream.forEach(action); // forEach() - конечная операция

Метод forEach() является конечной операцией. Это означает, что поток sortStream потреблен. Чтобы использовать поток заново, его нужно восстановить из источника — набора чисел AL.

После выполнения программа выдаст следующий результат

AL = [5, 4, 0, 5, 2, 9, 1, 3, 0, 2]
sorted AL = 9 5 5 4 3 2 2 1 0 0

 

4. Формирование новых потоков данных с помощью конвейера. Пример

Условие задачи. Задан поток чисел. Найти минимальное из чисел потока, которые больше 5 и меньше 20.

Решение. Для решения задачи нужно использовать подход конвейера для модификации потока. Используются две операции (методы):

  • операция (метод) filter() — промежуточная операция;
  • операция (метод) min() — конечная операция.

Текст решения задачи следующий

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class StreamAPI {

  public static void main(String[] args) {
    // Задача. Задан поток чисел. Найти минимальное из чисел потока, которые
    //         больше 5 и меньше 20.
    // 1. Создать набор чисел
    ArrayList<Integer> AL = new ArrayList<Integer>();

    AL.add(15);
    AL.add(13);
    AL.add(7);
    AL.add(1);
    AL.add(23);
    AL.add(6);
    AL.add(3);
    AL.add(11);
    AL.add(22);
    AL.add(2);

    // 2. Создать поток чисел на основании коллекции AL
    Stream<Integer> stream = AL.stream();
    System.out.println("AL = " + AL);

    // 3. Вызвать промежуточную операцию фильтрования - создается новый поток
    // 3.1. Создать метод сравнения - числа, которые больше 5
    Predicate<Integer> predicate = (a) -> a>5; // предикат (условие)

    // 3.2. Создать новый отфильтрованный поток - це помежуточная операция,
    //     после нее поток можно обрабатывать дальше
    Stream<Integer> filteredStream = stream.filter(predicate);

    // 4. Вызвать еще одну помежуточную операцию фильтрования - создается новый поток
    //    В этот раз фильтруются числа, которые меньше 20.
    predicate = (a) -> a<20;

    // 5. Отфильтровать тот же поток еще раз
    filteredStream = filteredStream.filter(predicate); // это опять же промежуточная операция

    // 6. Найти минимальное значение в новосозданном потоке
    Optional<Integer> min = filteredStream.min(Integer::compare); // это конечная операция

    // 7. Вывести минимальное значение на экран
    System.out.println("min = " + min.get());
  }
}

После выполнения программа выдаст следующий результат

AL = [15, 13, 7, 1, 23, 6, 3, 11, 22, 2]
min = 6

 

5. Примеры получения (открытия) потока данных. Методы stream(), parallelStream()

Пример реализует получения потока данных по различным видам коллекций. Для получения последовательного потока используется метод stream(). Для получения параллельного потока используется метод parallelStream().

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

public class StreamAPI {

  public static void main(String[] args) {
    // Задача. Задан набор чисел. Из этого набора создать поток
    // 1. Создать поток данных из коллекции типа ArrayList<T>
    // 1.1. Создать коллекцию чисел
    ArrayList<Integer> AL = new ArrayList<Integer>();

    AL.add(15);     AL.add(13);     AL.add(7);     AL.add(1);     AL.add(23);
    AL.add(6);     AL.add(3);     AL.add(11);     AL.add(22);     AL.add(2);

    // 1.2. Создать последовательный поток данных на основе коллекции AL
    Stream<Integer> stream = AL.stream();

    // 1.3. Создать параллельный поток данных из коллекции AL
    Stream<Integer> parallelStream = AL.parallelStream();

    // 2. Создать поток данных из массива целых чисел
    int[] AI = { 2, 5, 4, 8, -1, 3, 9 };
    IntStream streamInt = Arrays.stream(AI); // последовательный поток
    IntStream parallelStreamInt = streamInt.parallel(); // параллельный поток

    // 3. Создать поток данных из массива вещественных чисел
    double[] AD = { 0.5, -1.8, 3.77, 4.23, 21.7, -10.8 };
    DoubleStream streamDouble = Arrays.stream(AD); // последовательный поток к данному
    DoubleStream parallelStreamDouble = streamDouble.parallel(); // параллельный поток

    // 4. Создать поток данных из массива типа long
    Long[] ALong = { 282039l, 3239290l, 23902309l, 72083289L };
    Stream<Long> streamLong = Arrays.stream(ALong); // последовательный поток
    Stream<Long> parallelStreamLong = streamLong.parallel(); // параллельный поток
  }
}

 


Связанные темы