Інтерфейс StreamAPI. Кінцеві методи. Приклади використання. Методи collect(), count(), forEach(), max(), min(), reduce(), toArray()
Зміст
- 1. Метод collect(). Накопичення елементів у контейнері. Приклад
- 2. Метод count(). Отримати кількість елементів у потоці. Приклад
- 3. Метод forEach(). Виконати дію над кожним елементом потоку. Приклад
- 4. Метод max(). Повернути максимальний елементи в потоці даних. Приклад
- 5. Метод min(). Повернути мінімальнй елемент у потоці даних. Приклад
- 6. Метод reduce(). Отримати заданий результат з елементів потоку. Приклад
- 7. Метод toArray(). Створити масив з елементів у викликаючому потоці даних. Приклад
- Зв’язані теми
Пошук на інших ресурсах:
1. Метод collect(). Накопичення елементів у контейнері. Приклад
Метод collect() використовується для накопичення елементів з вихідного потоку в змінюваному контейнері. Контейнером може бути, наприклад, список, множина тощо. Метод collect() називається операцією змінного зведення.
Загальна форма методу наступна:
<R, A> R collect(Collector<? super T, A, R> collector)
тут
- R – тип результуючого контейнеру;
- T – тип елементів у вихідному (викликаючому) потоці даних;
- A – внутрішній накопичувальний тип;
- collector – функція накопичення, яка представляється лямбда-виразом. Функція визначає порядок виконання процесу накопичення.
Метод collect() є кінцевою операцією (terminal operation).
Приклад. У прикладі для потоку чисел типу Double продемонстровано наступні накопичення елементів:
- отримати з потоку даних множину типу set<Double>. Для реалізації цього використовується метод-колектор toSet() статичного класу Collectors пакету java.util.stream;
- отримати з потоку даних список типу List<Double>. У цьому випадку використовуєтсься метод toList() статичного класу Collectors.
import java.util.*; import java.util.stream.*; public class StreamAPI { public static void main(String[] args) { // Метод collect() - накопичення елементів у контейнері // 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. Отримати потік даних зі списку типу ArrayList<Double> Stream<Double> stream = AL.stream(); // 3. Виконати зворотню операцію - отримати множину типу Set<Double> Set<Double> set; set = stream.collect(Collectors.toSet()); // метод collect() // 4. Вивести множину set на екран System.out.print("set = { "); for (Double t : set) System.out.print(t + " "); System.out.println("}"); // 5. Отримати з множини set новий потік даних Stream<Double> stream2 = set.stream(); // 6. Отримати з нового потоку даних новий список типу List<Double> List<Double> AL2 = stream2.collect(Collectors.toList()); // 7. Вивести новий список на екран System.out.print("AL2 = [ "); for (Double t : AL2) System.out.print(t + " "); System.out.println("]"); } }
Результат виконання програми
set = { 2.8 -2.3 1.5 3.4 1.1 } AL2 = [ 2.8 -2.3 1.5 3.4 1.1 ]
⇑
2. Метод count(). Отримати кількість елементів у потоці. Приклад
Метод count() повертає кількість елементів у потоці. Загальна форма методу
long count()
Метод count() є кінцевою операцією.
Приклад використання методу count().
import java.util.*; import java.util.stream.*; public class StreamAPI { public static void main(String[] args) { // Метод count() - отримати кількість елементів у потоці // 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. Отримати кількість елементів long count = stream.count(); System.out.println("count = " + count); } }
Результат виконання програми
count = 5
⇑
3. Метод forEach(). Виконати дію над кожним елементом потоку. Приклад
Метод forEach() застосовується у випадках, коли над кожним елементом потоку потрібно виконати деяку дію (роботу). Наприклад, для зручного виведення значень елементів потоку на екран.
Загальна форма методу forEach() наступна:
void forEach(Consumer<? super T> action)
тут
- Consumer<? super T> – тип, що є стандартним функціональним інтерфейсом. У функціональному інтерфейсі поточний тип елементів обмежується деяким типом T (? super T);
- action – лямбда-вираз, що здійснює дію над окремим елементом потоку.
Метод forEach() є кінцевою операцією (terminal operation).
Приклад. У прикладі над кожним елементом потоку виконуються такі дії:
- помножити кожен елемент потоку на 2;
- вивести кожен елемент потоку на екран.
Для виконання дії метод forEach() отримує відповідний лямбда-вираз, що реалізує функціональний інтерфейс Consumer<Double>.
import java.util.*; import java.util.stream.*; import java.util.function.*; public class StreamAPI { public static void main(String[] args) { // Метод forEach() - виконати дію над кожним елементом потоку // 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. Створити посилання на стандартний функціональний інтерфейс // Consumer<T> та присвоїти йому лямбда-вираз Consumer<Double> action = (value) -> { value = value*2; // помножити кожен елемент потоку на 2 System.out.print(value + " "); // вивести елемент на екран }; // 4. Використати метод forEach() для виведення елементів потоку // на екран stream.forEach(action); } }
Результат виконання програми
3.0 5.6 -4.6 6.8 2.2
⇑
4. Метод max(). Повернути максимальний елементи в потоці даних. Приклад
Метод max() призначений для отримання елементу з максимальним значенням у потоці даних. Загальна форма методу наступна:
Optional<T> max(Comparator<? super T> comparator)
тут
- T – тип елементів потоку даних;
- Optional<T> – результат, що містить найбільше значення елементу в потоці даних. Щоб отримати елемент потрібно використати метод get();
- comparator – функція порівняння двох значень типу T, яка представлена лямбда-виразом;
- Comparator<? super T> – тип стандартного функціонального інтерфейсу Java, який використовується для порівняння двох значень узагальненого типу T.
Метод max() є кінцевою операцією.
Приклад. У прикладі оголошується клас Circle, який реалізує коло на координатній площині. Додатково формується потік даних типу Circle. Для вихідного потоку даних визначається коло в якого найбільша площа.
import java.util.*; import java.util.stream.*; // Клас, що реалізує коло на координатній площині class Circle { // Внутрішні поля класу private double x, y; // координата центру кола private double radius; // радіус кола // Конструктор public Circle(double x, double y, double radius) { this.x = x; this.y = y; this.radius = radius; } // Методи доступу public double getX() { return x; } public double getY() { return y; } public double getRadius() { return radius; } // Метод, що повертає площу круга public double Area() { return Math.PI*radius*radius; } } public class StreamAPI { public static void main(String[] args) { // Метод max() - визначає найбільше значення у потоці даних // 1. Створити набір даних типу Circle ArrayList<Circle> AL = new ArrayList<Circle>(); AL.add(new Circle(2.0, 5.0, 3.3)); AL.add(new Circle(2.5, 4.2, 3.1)); AL.add(new Circle(3.1, 4.1, 2.3)); AL.add(new Circle(4.0, 3.0, 4.3)); AL.add(new Circle(3.0, 2.0, 1.3)); // 2. Отримати потік даних типу Circle з набору AL Stream<Circle> stream = AL.stream(); // 3. Оголосити компаратор - метод порівняння двох значень типу Circle Comparator<Circle> comparator; // у лямбда-виразі потрібно порівняти два екземпляри типу Circle comparator = (circle1, circle2) -> (int)(circle1.Area()-circle2.Area()); // 4. Знайти коло з найбільшою площею Optional<Circle> maxCircle = stream.max(comparator); // 5. Вивести результат System.out.println("maxCircle = (" + maxCircle.get().getX() + "; " + maxCircle.get().getY() + "), radius = " + maxCircle.get().getRadius()); System.out.println("area = " + maxCircle.get().Area()); } }
Результат роботи програми
maxCircle = (4.0; 3.0), radius = 4.3 area = 58.088048164875275
⇑
5. Метод min(). Повернути мінімальнй елемент у потоці даних. Приклад
Метод min() дозволяє отримати елемент з найменшим значенням у потоці даних. Загальна форма методу має вигляд:
Optional<T> min(Comparator<? super T> comparator)
тут
- T – тип елементів потоку даних;
- Optional<T> – результат, що містить найменше значення елементу в потоці даних. Щоб отримати елемент потрібно використати метод get();
- comparator – функція порівняння двох значень типу T, яка представлена лямбда-виразом;
- Comparator<? super T> – тип стандартного функціонального інтерфейсу Java, який використовується для порівняння двох значень узагальненого типу T.
Метод min() є кінцевою операцією.
Приклад. У прикладі, на основі потоку відрізків (ліній), визначається відрізок з найменшою довжиною. У потоці даних кожен відрізок представлений екземпляром класу типу Line. Для конкретного відрізку задаються координати точок його кінців.
import java.util.*; import java.util.stream.*; // Клас, що реалізує відрізок, заданий координатами точок (x1, y1), (x2, y2) class Line { // Внутрішні поля класу private double x1, y1; private double x2, y2; // Конструктор public Line(double _x1, double _y1, double _x2, double _y2) { x1 = _x1; y1 = _y1; x2 = _x2; y2 = _y2; } // Методи доступу public double getX1() { return x1; } public double getY1() { return y1; } public double getX2() { return x2; } public double getY2() { return y2; } // Метод, що повертає довжину відрізка public double length() { return Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); } } public class StreamAPI { public static void main(String[] args) { // Метод min() - отримати мінімальне значення елементу в потоці // 1. Створити набір відрізків типу Line ArrayList<Line> AL = new ArrayList<Line>(); AL.add(new Line(5.0, 6.0, 8.0, 9.5)); AL.add(new Line(1.1, 2.3, 8.1, 5.4)); AL.add(new Line(3.2, 2.8, 1.7, 4.4)); AL.add(new Line(6.1, 2.5, 3.1, 4.0)); AL.add(new Line(-1.2, 3.8, -4.3, 2.5)); // 2. Отримати потік даних типу Line з набору AL Stream<Line> stream = AL.stream(); // 3. Оголосити компаратор - метод порівняння двох значень типу Line Comparator<Line> comparator; comparator = (line1, line2) -> { // порівняти довжини відрізків if (line1.length()>line2.length()) return 1; if (line1.length()<line2.length()) return -1; return 0; }; // 4. Знайти лінію з мінімальним значенням довжини Optional<Line> minLine = stream.min(comparator); // 5. Вивести результат System.out.println("minLine = ( " + minLine.get().getX1() + "; " + minLine.get().getY1() + "), ( " + minLine.get().getX2() + "; " + minLine.get().getY2() + ")"); System.out.println("length = " + minLine.get().length()); } }
Результат роботи програми:
minLine = ( 3.2; 2.8), ( 1.7; 4.4) length = 2.1931712199461315
⇑
6. Метод reduce(). Отримати заданий результат з елементів потоку. Приклад
Метод reduce() належить до операцій зведення так само як методи min(), max(), count(). В методі reduce() можна звести потік до єдиного значення за власно-розробленим критерієм.
Приклади таких критеріїв:
- обчислити суму елементів потоку даних;
- обчислити добуток елементів потоку даних;
- інші довільні критерії.
Метод reduce() має декілька загальних форм. Нижче наведено дві найбільш вживані:
Optional<T> reduce(BinaryOperator<T> метод_накопичення) T reduce(T значення_ідентичності, BinaryOperator<T> метод_накопичення)
тут
- T тип елементів потоку;
- BinaryOperator<T> – тип стандартного фунціонального інтерфейсу, в якому міститься метод apply(). Стандартний функціональний інтерфейс BinaryOperator<T> оголошується в пакеті java.util.function;
- метод_накопичення – це є лямбда-вираз, що реалізує функцію накопичення. Функція накопичення оперує двома значеннями та повертає деякий результат;
- значення_ідентичності – значення, що приводить до отримання того самого значення елементу з потоку даних. Наприклад, якщо значення елементу рівне x, то для операції просумовування значення_ідентичності=0 (0+x=x). Для операції множення значення_ідентичності=1 (1*x=x).
При написанні коду операції накопичення метод_накопичення потрібно дотримуватись наступних обмежень:
- обмеження без збереження стану означає, що обробка кожного елементу потоку здійснюється окремо;
- обмеження без втручання означає, що джерело даних не змінюється в тілі методу (операції);
- операція, що застосовується до елементів потоку повинна бути асоціативна (a+b=b+a).
Метод reduce() є кінцевою операцією (terminated operation).
Приклад. У прикладі визначається сума елементів потоку даних типу Double.
import java.util.*; import java.util.stream.*; import java.util.function.*; public class StreamAPI { public static void main(String[] args) { // Метод reduce() - застосовує функцію зведення до елементів потоку. // У прикладі обчислюється середнє арифметичне елементів потоку даних // 1. Створити набір даних типу Double ArrayList<Double> AL = new ArrayList<Double>(); AL.add(25.0); AL.add(17.0); AL.add(33.0); AL.add(18.0); AL.add(27.0); AL.add(11.0); // 2. Отримати потік даних типу Double з набору AL Stream<Double> stream = AL.stream(); // 3. Оголосити бінарний оператор, який повертає тип Double BinaryOperator<Double> accumulator; // 4. Для двох операндів реалізувати обчислення суми accumulator = (value1, value2) -> { return value1+value2; }; // 5. Викликати функцію reduce() та вивести результат Optional<Double> sum = stream.reduce(accumulator); System.out.println("sum = " + sum.get()); } }
Результат виконання програми
sum = 131.0
⇑
7. Метод toArray(). Створити масив з елементів у викликаючому потоці даних. Приклад
Метод toArray() призначений для конвертування потоку даних в масив типу Object[]. Метод має наступну загальну форму:
Object[] toArray()
Метод є кінцевою операцією.
Приклад. У прикладі потік даних типу Float конвертується у масив типу Object[].
import java.util.*; import java.util.stream.*; import java.util.function.*; public class StreamAPI { public static void main(String[] args) { // Метод toArray() - конвертувати потік даних в масив типу Object[] // 1. Створити набір даних типу Float ArrayList<Float> AL = new ArrayList<Float>(); AL.add(25.0f); AL.add(11.3f); AL.add(3.8f); AL.add(7.7f); AL.add(2.4f); AL.add(5.5f); // 2. Отримати потік даних типу Integer з набору AL Stream<Float> stream = AL.stream(); // 3. Отримати масив типу Object[] з потоку stream Object[] AF = stream.toArray(); // 4. Вивести результат System.out.print("AF = { "); for (int i=0; i<AF.length; i++) System.out.print(AF[i] + " "); System.out.println(" }"); } }
Результат виконання програми
AF = { 25.0 11.3 3.8 7.7 2.4 5.5 }
⇑
Зв’язані теми
- Потоки даних. Stream API. Загальна інформація
- Поняття кінцевої та проміжної операції
- Інтерфейс BaseStream. Приклади використання методів інтерфейсу
- Інтерфейс Stream<T>. Проміжні методи filter(), map(), mapToDouble(), mapToInt(), mapToLong(), sorted(). Приклади
⇑