Java. Потоки даних. Stream API. Загальна інформація




Потоки даних. Stream API. Загальна інформація

Перед вивченням даної теми рекомендується ознайомитись з наступними темами:


Зміст


Пошук на інших ресурсах:

1. Прикладний потоковий інтерфейс Stream API. Характерні особливості

Починаючи з версії JDK 8 в мові Java було введено засоби роботи з потоками даних, які отримали назву прикладний потоковий інтерфейс API (Stream Application Programming Interface). Робота з засобами Stream API базується на використанні лямбда-виразів.

Характерними особливостями цього інтерфейсу є використання різноманітних операцій над потоками даних. До цих операцій можна віднести:

  • пошук даних;
  • модифікація даних;
  • фільтрування потоку даних для отримання нового потоку;
  • сортування даних;
  • інші різноманітні маніпуляції над даними.

Операції над потоками даних виконуються на основі формування відповідних запитів. Інтерфейс Stream API добре підходить для обробки великих масивів даних з застосуванням механізмів розпаралелення.

 

2. Потік даних. Визначення

Під поняття “потік даних” мається на увазі канал передачі даних. Для потоку даних визначається поняття джерела даних. Джерелами даних можуть бути масив, колекція, список тощо. Потік даних оперує цими джерелами. Потік даних ще можна визначити як послідовність об’єктів.

У самому потоці дані не зберігаються, а тільки переміщуються при їх обробці (фільтруванні, сортуванні тощо). При обробці потоку даних джерело даних не змінюється. Це означає, що при сортуванні даних створюється новий посортований потік даних, а початкове джерело залишається несортованим.

 

3. Інтерфейси з пакету java.util.stream
3.1. Інтерфейс BaseStream. Методи

Засоби роботи з потоками даних Stream API є складовою пакету java.util.stream. Даний пакет містить набір потокових інтерфейсів, які утворюють ієрархію.

Базовим потоковим інтерфейсом є BaseStream, який має наступне оголошення

interface BaseStream<T, S extends BaseStream<T, S>>

тут

  • T – тип елементів у потоці даних;
  • S – тип потоку даних, який розширює інтерфейс BaseStream.

В інтерфейсі BaseStream оголошується ряд методів, які перераховані нижче.

1. Закрити потік даних

void close()

Метод закриває викликаючий потік даних. Обов’язково потрібно закривати потоки даних, що зв’язані з файлами.

2. Визначити, чи потік даних є паралельним.

boolean isParallel()

Метод повертає true, якщо потік даних є паралельним.

3. Отримати ітератор для потоку даних

Iterator<T> iterator()

тут

  • T – тип елементів потоку даних.

Метод отримує ітератор для потоку даних і повертає посилання на нього. Метод є кінцевою операцією.

4. Задати обробник події закриття потоку.

S onClose(Runnable handler)

тут

  • S – тип новоствореного потоку даних;
  • handler – метод, що містить код який повинен виконатись при закритті потоку.

Метод onClose() повертає новий потік даних з заданим обробником події закриття. Вказаний обробник викликається при закритті потоку даних. Метод є проміжною операцією.

5. Повернути паралельний потік даних

S parallel()

тут

  • S – тип новоствореного паралельного потоку даних.

Метод повертає паралельний потік даних базуючись на основі викликаючого потоку даних. Якщо викликаючий потік даних є паралельним, то він і повертається. Це є проміжна операція.

6. Повернути послідовний потік даних

S sequential()

тут

  • S – тип новоствореного послідовного потоку даних.

Метод повертає послідовний потік даних на основі викликаючого потоку даних. Якщо викликаючий потік даних вже є послідовним, то цей потік і повертається. Метод є проміжною операцією.

7. Отримати ітератор-розділювач

Spliterator<T> spliterator()

тут

  • T – тип елементів потоку даних.

Метод отримує ітератор-розділювач для потоку даних і повертає посилання на нього. Метод є кінцевою операцією.

8. Повернути невпорядкований потік даних

S unordered()

тут

  • S – тип результуючого невпорядкованого потоку даних.

Метод повертає невпорядкований потік даних на основі викликаючого потоку даних. Якщо викликаючий потік даних вже є невпорядкованим, то саме він і повертається. Метод є проміжною операцією.

 

3.2. Інтерфейс Stream. Огляд методів

Від базового інтерфейсу BaseStream успадковані декілька інтерфейсів. Найбільш вживаним з них є узагальнений інтерфейс Stream<T>, який має наступне оголошення

interface Stream<T>

тут

  • T – тип елементів у потоці даних.

В інтерфейсі Stream<T> визначаються ряд методів, які можна використовувати при обробці потоків даних. Ці методи описуються нижче.

1. Накопичити елементи в контейнері

<R, A> R collect(Collector<? super T, A, R> collector)

тут

  • R – тип контейнеру, в якому накопичуються елементи;
  • T – тип елементу з викликаючого потоку даних;
  • A – внутрішній накопичувальний тип;
  • collector – функція накопичення, яка представляється лямбда-виразом. Функція визначає порядок виконання процесу накопичення.

Метод реалізує операцію змінюваного зведення. Метод накопичує елементи в змінюваному контейнері і повертає цей контейнер.

Метод collect() є кінцевою операцією.

2. Отримати кількість елементів у потоці

long count()

Метод count() є кінцевою операцією.

3. Виробити новий потік даних на основі фільтру

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

тут

  • T – тип елементів потоку даних;
  • predicate – умова, на основі якої формується новий потік даних.

Метод filter() є проміжною операцією.

4. Виконати дію над кожним елементом потоку даних

void forEach(Consumer<? super T> action)

тут

  • action – посилання на стандартний функціональний інтерфейс Consumer<T>. В інтерфейсі Consumer<T> реалізовано метод, який виконує деяку дію над елементом типу T. Ця дія буде застосовуватись до кожного елементу потоку даних.

Метод forEach() є кінцевою операцією.

5. Застосувати вказану функцію відображення для узагальненого типу R

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

тут

  • map_function – функція відображення, яка застосовується до елементів з викликаючого потоку даних.

Результатом роботи функції є новий потік даних, що містить ці елементи. Ця функція є проміжною операцією.

6. Застосувати функцію відображення для потоку типу DoubleStream

DoubleStream mapToDouble(ToDoubleFunctin <? super T> map_function)

тут

  • map_function – функція відображення, яка застосовується до елементів викликаючого потоку даних.

Результатом роботи функції mapToDouble() є новий потік даних типу DoubleStream. Тип елементів у потоці встановлюється Double. Це є проміжна операція.

7. Застосувати функцію відображення для потоку типу IntStream

IntStream mapToInt(ToIntFunctin <? super T> map_function)

тут

  • map_function – функція відображення, яка застосовується до елементів викликаючого потоку даних.

Результатом роботи функції mapToInt() є новий потік даних типу IntStream. Тип елементів у потоці встановлюється Integer. Це є проміжна операція.

8. Застосувати функцію відображення для потоку типу LongStream

LongStream mapToLong(ToLongFunctin <? super T> map_function)

тут

map_function – функція відображення, яка застосовується до елементів викликаючого потоку даних.

На основі заданої функції відображення створюється новий потік даних типу LongStream що містить ці елементи. Це є проміжна операція.

9. Пошук мінімального значення в потоці даних типу T

Optional<T> min(Comparator<? super T> comparator)

тут

  • comparator – посилання на метод, в якому описується код порівняння двох елементів типу T. На основі коду цього методу визначається елемент з мінімальним значенням у потоці даних.

Метод min() є кінцевою операцією.

10. Пошук максимального значення в потоці даних типу T

Optional<T> max(Comparator<? super T> comparator)

тут

  • comparator – посилання на метод, в якому описується код порівняння двох елементів типу T. На основі коду цього методу визначається елемент з максимальним значенням у потоці даних.

Метод max() є кінцевою операцією.

11. Реалізувати звід (зведення) для елементів у викликаючому потоці даних

T reduce (T identityVal, BinaryOperator<T> storage)

тут

  • identityVal – значення ідентичності, яке використовується у поєднанні з функцією storage, для отримання такого ж елементу без змін;
  • storage – функція, яка оперує двома значеннями типу T і повертає результат.

Метод reduce() є кінцевою операцією.

12. Сортування потоку даних

Stream<T> sorted()

Метод sorted() призначений для сортування потоку даних в природному порядку (за зростанням елементів). Якщо потрібно змінити порядок сортування елементів, то потрібно реалізувати стандартний функціональний інтерфейс Comparator<T> і передати лямбда-вираз у даний метод. Це є кінцева операція.

13. Створити масив з елементів у викликаючому потоці даних

Object[] toArray()

Метод toArray() використовується для конвертування потоку даних в масив типу Object[]. Метод дозволяє оперувати будь-якими типами (Integer, Double, Float і т.д.).

 


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