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()
и rangeClosed()
.
Разница в том, что конец range()
является эксклюзивным, в то время как в rangeClosed()
он включен .
Методы диапазона доступны только для IntStream
и LongStream
.
Мы можем использовать диапазон как причудливую форму цикла 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 .