«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.