«1. Введение

Stream API был одной из ключевых функций, добавленных в Java 8.

Вкратце, API позволяет нам обрабатывать коллекции и другие последовательности элементов — удобно и более эффективно — предоставляя декларативный API.

2. Примитивные потоки

Потоки в основном работают с коллекциями объектов, а не с примитивными типами.

К счастью, для обеспечения возможности работы с тремя наиболее часто используемыми примитивными типами — int, long и double — стандартная библиотека включает три специализированные реализации примитивов: IntStream, LongStream и DoubleStream.

Примитивные потоки ограничены в основном из-за накладных расходов на упаковку и потому, что создание специализированных потоков для других примитивов во многих случаях не так уж полезно.

3. Арифметические операции

Давайте начнем с нескольких интересных методов для часто используемых арифметических операций, таких как min, max, sum и Average:

int[] integers = new int[] {20, 98, 12, 7, 35};
int min = Arrays.stream(integers)
  .min()
  .getAsInt(); // returns 7

Давайте теперь рассмотрим приведенный выше фрагмент кода, чтобы понять, что продолжается.

Мы создали наш IntStream с помощью java.util.Arrays.stream(int[]), а затем использовали метод min() для получения наименьшего целого числа как java.util.OptionalInt и, наконец, вызвали getAsInt() для получения int ценность.

Другой способ создать IntStream — использовать IntStream.of(int…). Метод max() вернет наибольшее целое число:

int max = IntStream.of(20, 98, 12, 7, 35)
  .max()
  .getAsInt(); // returns 98

Далее — чтобы получить сумму целых чисел, мы просто вызываем метод sum(), и нам не нужно использовать getAsInt(), так как он уже возвращает результат в виде значения int:

int sum = IntStream.of(20, 98, 12, 7, 35).sum(); // returns 172

Мы вызываем метод medium(), чтобы получить среднее значение целочисленных значений, и, как мы видим, мы должны использовать getAsDouble(), так как он возвращает значение типа double.

double avg = IntStream.of(20, 98, 12, 7, 35)
  .average()
  .getAsDouble(); // returns 34.4

4. Диапазон

Мы также можем создать IntStream на основе диапазона:

int sum = IntStream.range(1, 10)
  .sum(); // returns 45
int sum = IntStream.rangeClosed(1, 10)
  .sum(); // returns 55

Как показано в приведенном выше фрагменте кода, существует два способа создания диапазона целочисленных значений range() и диапазонзакрыт().

Разница в том, что конец range() является эксклюзивным, в то время как в rangeClosed() он включен.

Методы Range доступны только для IntStream и LongStream.

Мы можем использовать range как причудливую форму цикла for-each:

IntStream.rangeClosed(1, 5)
  .forEach(System.out::println);

Их использование в качестве замены цикла for-each хорошо тем, что мы также можем воспользоваться преимуществом параллельного выполнения: ~~ ~

IntStream.rangeClosed(1, 5)
  .parallel()
  .forEach(System.out::println);

Как бы ни были полезны эти причудливые циклы, все же лучше использовать традиционные циклы for вместо функциональных для простых итераций из-за простоты, удобочитаемости и производительности в некоторых случаях.

5. Упаковка и распаковка

Бывают случаи, когда нам нужно преобразовать примитивные значения в их эквиваленты-оболочки.

В этих случаях мы можем использовать метод boxed():

List<Integer> evenInts = IntStream.rangeClosed(1, 10)
  .filter(i -> i % 2 == 0)
  .boxed()
  .collect(Collectors.toList());

Мы также можем преобразовать поток класса-оболочки в примитивный поток:

// returns 78
int sum = Arrays.asList(33,45)
  .stream()
  .mapToInt(i -> i)
  .sum();

Мы всегда можем использовать методы mapToXxx и flatMapToXxx для создания примитивных потоков.

6. Заключение

Java Streams — очень мощное дополнение к языку. Здесь мы едва коснулись примитивных потоков, но вы уже можете использовать их для продуктивной работы.

И, как всегда, образцы кода можно найти на GitHub.