Java. Интерфейс StreamAPI. Конечные методы. Примеры использования

Интерфейс StreamAPI. Конечные методы. Примеры использования. Методы collect(), count(), forEach(), max(), min(), reduce(), 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 }

 


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