Java. Интерфейс Stream. Промежуточные методы интерфейса




Интерфейс Stream<T>. Промежуточные методы интерфейса. Примеры использования. Методы filter(), map(), mapToDouble(), mapToInt(), mapToLong(), sorted()


Содержание


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

1. Понятие промежуточной операции. Промежуточные операции без сохранения состояния и с сохранением состояния

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

Для промежуточных операций характерно:

  • выполнение операции без сохранения состояния. В этом случае каждый элемент обрабатывается независимо от других. Например, в методе filter() выборка элемента в новый поток не зависит от значений других элементов исходного потока;
  • выполнение операции с сохранением состояния. В этом случае обработка элемента зависит от особенностей других элементов. Примером этому может служить сортировка элементов, где позиция элемента зависит от значений других элементов (метод sorted()).

При выполнении промежуточных операций состояние сохранения (не сохранения) является важным в случае параллельной обработки потоков данных. Это связано с тем, что операции с сохранением состояния могут быть выполнены за несколько проходов. В то же время, операции без сохранения состояния могут быть выполнены за один проход.

 

2. Метод filter(). Получить новый поток согласно условию (предикату). Пример

Метод filter() предназначен для получения нового потока согласно заданному условию. Метод имеет следующую общую форму

Stream<T> filter(Predicate<? super T> predicate)

здесь

  • T – тип элементов потока;
  • Stream<T> – результирующий отфильтрованный поток;
  • Predicate<? super T> – предикат, определяющий условие фильтрации.

Метод filter() есть промежуточной операцией (intermediate operation).

Пример. В примере метод filter() используется для создания нового потока чисел типа Double. В новом потоке формируются числа, больше значения 2.5.

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

public class StreamAPI {

  public static void main(String[] args) {
    // Метод filter() - получить новый поток согласно предикату
    // 1. Создать набор чисел
    ArrayList<Double> AL = new ArrayList<Double>();
    AL.add(1.5);
    AL.add(2.8);
    AL.add(-2.3);
    AL.add(3.4);
    AL.add(1.1);

    // 2. Получить поток данных
    Stream<Double> stream = AL.stream();

    // 3. Создать предикат для метода filter()
    Predicate<Double> predicate;

    // 4. Указать лямбда-выражение для предиката
    //    В лямбда-выражении указывается условие отбора: число больше 2.5
    predicate = (n) -> n>2.5;


    // 5. Получить новый поток
    Stream<Double> streamFiltered = stream.filter(predicate);

    // 6. Вывести новый поток с помощью итератора
    Iterator<Double> it = streamFiltered.iterator();
    System.out.println("Filtered stream: ");
    while (it.hasNext())
      System.out.print(it.next()+" ");
  }
}

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

Filtered stream:
2.8 3.4

 

3. Метод map(). Применить указанную функцию отображения к элементам вызывающего потока. Пример

Метод map() используется для применения к потоку некоторой функции отображения. Метод возвращает результирующий поток. Метод имеет следующую общую форму:

R Stream<R> map(Function<? super T, ? extends R> mapper)

здесь

  • R – тип элементов результирующего потока;
  • T – тип элементов исходного потока;
  • mapper – лямбда-выражение, которое выполняет действие над элементами исходного потока;
  • Function<? super T, ? extends R> – тип, соответствующий стандартному функциональному интерфейсу Java. В этом интерфейсе определен метод apply(), который выполняет некоторую функцию над элементами потока.

Метод есть промежуточной операцией (intermediate operation).

Пример. В примере демонстрируется применение метода map() для следующих задач:

  • для заданного потока строк сформировать новый поток целых чисел. Каждое число результирующего потока является длиной соответствующей строки исходного потока;
  • для заданного потока строк сформировать новый поток строк. Каждая строка результирующего потока является реверсивной (обернутой) к соответствующей строке исходного потока.

 

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

public class StreamAPI {

  public static void main(String[] args) {
    // Метод map() - применить функцию отображения для элементов потока
    // 1. Создать набор строк
    ArrayList<String> AL = new ArrayList<String>();
    AL.add("dictionary");
    AL.add("month");
    AL.add("face");
    AL.add("table");
    AL.add("object");
    AL.add("string");

    // 2. Получить поток
    Stream<String> stream = AL.stream();

    // 3. Объявить ссылку на функциональный интерфейс Function<T, R>
    Function<String, Integer> mapper;

    // 4. Сформировать новый поток, в котором каждый элемент есть длиной соответствующей строки
    //    исходного потока, например dictionary->10, month->5, face->4, ...
    // 4.1. Объявить соответствующее лямбда-выражение
    mapper = (str) -> {
      return str.length();
    };

    // 4.2. Создать новый поток
    Stream<Integer> streamInt = stream.map(mapper);

    // 4.3. Вывести новый поток с помощью итератора
    Iterator<Integer> iterator = streamInt.iterator();
    System.out.print("streamInt = [ ");
    while (iterator.hasNext()) {
      System.out.print(iterator.next() + " ");
    }
    System.out.println("]");

    // ---------------------------------------------------------------
    // 5. Сформировать новый поток строк, в котором
    //   строки отображены в обратном порядке
    // 5.1. Объявить функцию отображения и присвоить ей лямбда-выражение
    Function<String, String> mapperStr = (str) -> {
      String str2 = "";
      for (int i=0; i<str.length(); i++)
        str2 = str2 + str.charAt(str.length() - i - 1);
      return str2;
    };

    // 5.2. Создать новый поток реверсированных строк
    Stream<String> streamReverseStr = AL.stream().map(mapperStr);

    // 5.3. Вывести новый поток методом forEach()
    Consumer<String> action = (n) -> {
      System.out.print(n + " ");
    };
    System.out.print("streamReverseStr = [ ");
    streamReverseStr.forEach(action);
    System.out.println("]");
  }
}

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

streamInt = [ 10 5 4 5 6 6 ]
streamReverseStr = [ yranoitcid htnom ecaf elbat tcejbo gnirts ]

 

4. Метод mapToDouble(). Применить функцию отображения к потоку данных типа DoubleStream

Метод mapToDouble() предназначен для применения функции отображения к потоку данных типа Double. Общая форма метода следующая:

DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)

здесь

  • T – тип элементов исходного потока данных;
  • DoubleStream – последовательность элементов примитивного типа double, которая есть специализацией потока Stream;
  • ToDoubleFunction<? super T> – функциональный интерфейс, содержащий метод applyAsDouble(). Метод получает данные обобщенного типа T и возвращает результат типа double. Это специализация функционального интерфейса Function, которая возвращает тип double.

Метод mapToDouble() является промежуточным операцией.

Пример. В примере метод mapToDouble() используется для получения потока данных типа Double из потока данных типа Integer. Над каждым элементом входного потока данных типа Integer выполняется операция взятия корня квадратного.

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

public class StreamAPI {

  public static void main(String[] args) {
    // Метод mapToDouble() - получить поток данных типа Double на основании функции отображения
    // 1. Создать набор чисел типа Integer
    ArrayList<Integer> AL = new ArrayList<Integer>();
    AL.add(25);
    AL.add(36);
    AL.add(144);
    AL.add(256);
    AL.add(225);
    AL.add(81);

    // 2. Получить поток данных типа Integer из набора AL
    Stream<Integer> stream = AL.stream();

    // 3. Реализовать функцию отображения для данных типа Integer
    //    Нужно получить корень квадратный из каждого элемента потока.
    ToDoubleFunction<Integer> mapper;

    // Лямбда-выражение, возвращающее корень квадратный из целого числа
    mapper = (value) -> Math.sqrt(value);

    // 4. Вызвать метод mapToDouble() - получить новый поток
    DoubleStream streamDouble = stream.mapToDouble(mapper);

    // 5. Вывести новый поток с помощью итератора
    Iterator<Double> it = streamDouble.iterator();
    System.out.print("streamDouble = [ ");
    while (it.hasNext())
      System.out.print(it.next() + " ");
    System.out.println("]");
  }
}

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

streamDouble = [ 5.0   6.0 12.0 16.0   15.0 9.0 ]

 

5. Метод mapToInt(). Применить функцию отображения к потоку данных типа IntStream. Пример

Метод mapToInt() предназначен для применения функции отображения к потоку данных типа Integer. Общая форма метода следующая

IntStream mapToInt(ToIntFunction<? super T> mapper)

здесь

  • IntStream – последовательность значений типа int, которая является примитивной int-реализацией типа Stream;
  • ToIntFunction<? super T> – тип функции отображения, которая конвертирует данные обобщенного типа T в данные типа int. Это специализация стандартного интерфейса Function для типа int;
  • mapper — функция отображения, которая применяется к потоку данных обобщенного типа T.

Пример. В примере исходный поток типа Double конвертируется в результирующий поток типа Integer. Каждый элемент исходного потока округляется к ближайшему целому.

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

public class StreamAPI {

  public static void main(String[] args) {
    // Метод mapToInt() - получить поток данных типа int на основании функции отображения
    // 1. Создать набор чисел типа Double
    ArrayList<Double> AL = new ArrayList<Double>();
    AL.add(25.7);
    AL.add(36.88);
    AL.add(144.05);
    AL.add(256.2);
    AL.add(225.7);
    AL.add(81.4);

    // 2. Получить поток данных типа Double из набора AL
    Stream<Double> stream = AL.stream();

    // 3. Реализовать функцию отображения для данных типа Integer.
    //    Функция отображения
    ToIntFunction<Double> mapper;

    // Лямбда-выражение, возвращающее результат типа Integer,
    // Каждый элемент типа Double округляется к ближайшему целому
    mapper = (value) -> {
      return (int)(value+0.5);
    };

    // 4. Вызвать метод mapToInt() - получить новый поток
    IntStream streamInt = stream.mapToInt(mapper);

    // 5. Вывести новый поток с помощью итератора
    Iterator<Integer> it = streamInt.iterator();
    System.out.print("streamInt = [ ");
    while (it.hasNext())
      System.out.print(it.next() + " ");
    System.out.println("]");
  }
}

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

streamInt = [ 26 37 144 256 226   81 ]

 

6. Метод mapToLong(). Применить функцию отображения к потоку данных типа LongStream. Пример

Метод mapToLong() используется в случаях, когда нужно получить поток данных типа Long. В методе для некоторого типа T применяется функция отображения, которая возвращает результат типа Long. Общая форма метода следующая:

LongStream mapToLong(ToLongFunction<? super T> mapper)

здесь

  • mapper – функция отображения, которая представлена в виде лямбда-выражения. Функция получает значение элемента обобщенного типа T и возвращает результат;
  • ToLongFunction<? super T> – тип функции отображения, предназначенной для получения данных типа Long;
  • LongStream – тип, отражающий последовательность элементов типа long.

Метод mapToLong() есть промежуточной операцией.

Пример. В примере числа типа Integer исходного потока конвертируются в результирующий поток типа Long. Каждое число потока возводится в квадрат.

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

public class StreamAPI {

  public static void main(String[] args) {
    // Метод mapToLong() - получить поток данных типа Long на основании функции отображения
    // 1. Создать набор чисел типа Integer
    ArrayList<Integer> AL = new ArrayList<Integer>();
    AL.add(25);
    AL.add(3688);
    AL.add(14405);
    AL.add(2562);
    AL.add(2257);
    AL.add(8144);

    // 2. Получить поток данных типа Integer из набора AL
    Stream<Integer> stream = AL.stream();

    // 3. Реализовать функцию отоображения для данных типа Integer
    //    Функция отображения возвращает квадраты чисел типа Integer
    ToLongFunction<Integer> mapper;

    // Лямбда-выражение, возвращающее результат типа Long,
    // каждый элемент типа Integer возводится в квадрат
    mapper = (value) -> {
      return (long)value * (long)value;
    };

    // 4. Вызвать метод mapToLong() - получить новый поток
    LongStream streamLong = stream.mapToLong(mapper);

    // 5. Вывести новый поток с помощью итератора
    Iterator<Long> it = streamLong.iterator();
    System.out.print("streamLong = [ ");
    while (it.hasNext())
      System.out.print(it.next() + " ");
    System.out.println("]");
  }
}

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

streamLong = [ 625 13601344 207504025   6563844 5094049 66324736   ]

 

7. Метод sorted(). Отсортировать элементы в порядке возрастания и убывания. Пример

Метод sorted() позволяет получить отсортированный поток из вызывающего потока. Метод имеет две перегруженные реализации.

Общая форма первой реализации метода следующая:

Stream<T> sorted()

здесь

  • T – тип элементов потока;
  • Stream<T> – результирующий отсортированный поток. Элементы сортируются по возрастанию (естественный порядок).

Общая форма второй реализации метода следующая:

Stream<T> sorted(Comparator<? super T> comparator)

здесь

    • T – тип элементов потока;
    • Stream<T> – результирующий поток, отсортированный в порядке убывания;
  • Comparator<? super T> – тип стандартного функционального интерфейса Java, который содержит метод, сравнивающий два значения;
  • comparator – метод, в который вписан код лямбда-выражения. Лямбда-выражение получает два параметра и сравнивает их. Выражение возвращает целое число (<0, == 0,> 0) в зависимости от того, первый параметр меньше (равен, больше) чем второй параметр.

Метод sorted() является промежуточной операцией.

Пример. В примере демонстрируется применения метода sorted() для сортировки строк:

  • в порядке возрастания элементов (естественный порядок);
  • в порядке убывания элементов.

 

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

public class StreamAPI {

  public static void main(String[] args) {
    // Метод sorted() - отсортировать элементы в естественном порядке (по возрастанию)
    // 1. Создать набор строк
    ArrayList<String> AL = new ArrayList<String>();
    AL.add("dictionary");
    AL.add("month");
    AL.add("face");
    AL.add("table");
    AL.add("object");
    AL.add("string");

    // 2. Сортировка в естественном порядке
    // 2.1. Получить поток данных из списка типа ArrayList<String>
    Stream<String> stream = AL.stream();

    // 2.2. Вызвать метод sorted()
    Stream<String> streamSorted = stream.sorted();

    // 2.3. Вывести элементы потока streamSorted с помощью итератора
    Iterator<String> iterator = streamSorted.iterator();
    System.out.print("streamSorted = [ ");
    while (iterator.hasNext())
      System.out.print(iterator.next() + " ");
    System.out.println("]");

    // 3. Отсортировать элементы в порядке убывания
    // 3.1. Объявить компаратор - метод, сравнивающий две строки
    Comparator<String> comparator;

    // 3.2. Присвоить компаратору лямбда-выражение сравнения в обратном порядке
    comparator = (str1, str2) -> {
      return str2.compareTo(str1);
    };

    // 3.3. Отсортировать строки - метод sorted() с параметром компаратора
    Stream<String> streamSortedDesc = AL.stream().sorted(comparator);

    // 3.4. Вывести строки с помощью итератора
    iterator = streamSortedDesc.iterator();
    System.out.print("streamSortedDesc = [ ");
    while (iterator.hasNext())
      System.out.print(iterator.next()+" ");
    System.out.println("]");
  }
}

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

streamSorted = [ dictionary face month object string table ]
streamSortedDesc = [ table string object month face dictionary ]

 


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