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 ]

 


Зв’язані теми