В этой статье мы поговорим о API-интерфейсе Stream, добавленном в Java 8, и о том, как он изменил способ работы программ на Java. Это добавило аккуратности, а также сделало код более читабельным. Это помогает проводить функциональное программирование на Java. Итак, без лишних слов, давайте узнаем больше об этом драгоценном камне Java!
Вам также может понравиться:
Ваше руководство по потокам Java [Учебники и статьи]
Что такое Stream API?
Stream API, включенный в Java 8, используется для обработки collections of Objects
. Это поток объектов, на котором в конвейере применяются различные методы для получения результата. Проще говоря, он проходит через данные Collection<Object>
и применяет различные методы Object
для объединения их в желаемый результат, не влияя на оригинал Object
.
Что обеспечивает поток
Stream ничего не хранит; вместо этого он работает с данным Collection
, Array
или вводом / выводом (да, вы можете использовать его для ввода / вывода). Не изменяя исходные данные, он применяет методы к данным. Ленивая оценка помогает добавить несколько intermediate operations
без запуска полного потока. Оценивается только после добавления terminal operation
. это добавляет аккуратность и делает коды чище. Создайте более полный код. И многое другое
Что нужно знать, прежде чем идти
Stream позволяет практиковать функциональное программирование на Java. Это означает, что вам должно быть удобно с функциональными интерфейсами и лямбда-выражениями. Если вы хорошо понимаете эти концепции, никто не сможет помешать вам понять API-интерфейс Stream. Вы можете найти примеры кода на GitHub или просмотреть полный проект здесь .
Как стримить
Потоки могут быть реализованы несколькими способами. Несколько фокусов:
- Коллекции
stream()
иparallelStream()
методы - С помощью
Arrays.stream(Object[])
- Статические фабричные методы записи потока , такие как
Stream.of(Object[])
,IntStream.range(int, int)
илиStream.iterate(Object, UnaryOperator)
- Потоковая строка файла
BufferedReader.lines()
- Потоки случайных чисел могут быть получены из
Random.ints()
.
Первая и вторая пули — это то, что мы используем чаще всего. Для нашего обсуждения мы будем придерживаться Collection#steam()
метода. Например:
Джава
1
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5,
2
6, 7, 8, 9, 10);
3
//start the stream
4
list.stream()
5
// multiplying each number by 2
6
.map(n -> n * 2)
7
//taking forward only even numbers
8
.filter(n -> n % 2 == 0)
9
//printing each even number
10
.forEach(n -> System.out.println(n));
Операции с потоковым конвейером
Мы можем выполнить две операции над конвейером Stream:
- Промежуточная операция
- Терминальная операция
Промежуточная операция преобразует или фильтрует объект в предыдущем конвейере. Эти методы — не что иное, как Functional Interfaces
мы упоминали ранее. Вы должны ознакомиться с ними, чтобы легко понять эту операцию без каких-либо проблем.
Stream не будет выполнять промежуточные операции, пока не будет вызвана терминальная операция . Это указание потоку запустить конвейер. Как только мы вызываем операцию терминала, конвейер заканчивается, и он возвращает желаемый результат. После работы терминала мы не можем использовать промежуточные операции, так как конвейер закончился. Он может быть вызван только один раз в конце конвейера. Однако вы можете снова запустить поток, если терминальная операция возвращает коллекцию — что я категорически против. Вы можете в значительной степени делать все внутри одного потока.
Промежуточные операции
В конвейере Stream у нас может быть несколько промежуточных операций, которые преобразуют или фильтруют данные, которые будут поступать в следующий канал, не влияя на фактические данные в Коллекции. Эти операции возвращают поток желаемого результата вместо фактического результата, через который мы можем добавить более промежуточную операцию, не нарушая конвейер потока. Мы будем говорить об этом map
и filter
в этом посте, и в другой статье. Методы map
и filter
те, которые вам понадобятся в большинстве случаев.
Эти методы применяются один за другим к элементам конвейера, а не ко всем сразу.
отображение (функция)
Думайте о map(Function)
функции преобразования, которая преобразует данный объект в другой. Он принимает a Function
в качестве параметра, который принимает элемент в потоке в качестве входных данных, выполняет некоторую операцию, которую мы ему сообщаем, и возвращает Stream результирующего объекта. Если мы посмотрим на наш предыдущий пример, он умножает каждое число на 2. У нас может быть другая карта, которая может добавить 2 к числу, а затем еще одну карту, чтобы преобразовать ее в другой объект, а затем еще один, и так далее — вы получите идея.
Джава
xxxxxxxxxx
1
list.stream()
2
// 1. multiplying each number by 2
3
.map(n -> n * 2)
4
// 2. Converting to string.
5
.map(n -> "CodersTea.com-Post No : " + n)
6
//3. Now you will be working on the
7
//stream of String
8
//4. we add another string to the previous
9
.map(string -> string + ". Another String")
10
//print the end result
11
.forEach(System.out::println);
В приведенном выше примере посмотрите, как мы преобразовали список Integer
в String
. Давайте разберемся с этим:
- Умножая каждое число на 2. Итак, 1 становится 2, 2 становится 4 и так далее.
- Мы прикрепили номер к строке, результатом которой стал объект String.
- Предыдущая карта сделала
Stream<Integer>
toStream<String>
из-за возвращаемого типа строки. - Мы снова прикрепили еще одну строку для выполнения какой-либо операции.
- Наконец печать результатов, которая выглядит следующим образом:
Вывод:
Джава
x
1
CodersTea.com-Post No : 2. Another String
2
CodersTea.com-Post No : 4. Another String
3
CodersTea.com-Post No : 6. Another String
4
CodersTea.com-Post No : 8. Another String
5
CodersTea.com-Post No : 10. Another String
6
CodersTea.com-Post No : 12. Another String
7
CodersTea.com-Post No : 14. Another String
8
CodersTea.com-Post No : 16. Another String
9
CodersTea.com-Post No : 18. Another String
10
CodersTea.com-Post No : 20. Another String
фильтр (предикат)
Фильтр, как следует из названия, фильтрует элементы до того, как достигнет следующей трубы. Проще говоря, если элемент удовлетворяет условию, он позволяет ему пройти; в противном случае он не позволит выполнить следующую операцию. Это принимает Predicate
в качестве параметра.
Джава
xxxxxxxxxx
1
long count = list.stream()
2
// 1. Need only even numbers
3
.filter(n -> n % 2 == 0)
4
// 2. Checking if the Number is
5
// greater than 5
6
.filter(n -> n > 5)
7
//3. multiplying the number by 5
8
// just to show how we can use multiple
9
// Intermediate operation together
10
.map(n -> n * 5)
11
// 4. Any number less than 50
12
.filter(n -> n < 50)
13
// Counting how much elements
14
// survived teh pipeline
15
.count();
16
System.out.println("Total " + count + " numbers survived the storm");
Так много фильтров. Хорошо, давайте разберемся с этим, не так ли?
- Возьмите только четные числа впереди.
- Вперед только номера больше 5
- Умножьте число на 5. Опять же, просто чтобы показать, что вы можете иметь несколько промежуточных операций в одном потоке.
- Разрешить числа, которые меньше 50.
- Наконец,
count
(Terminal Operation) сколько выжило после такой большой фильтрации.
Вывод:
Джава
xxxxxxxxxx
1
Total 2 numbers survived the storm
Терминальная операция
До сих пор мы видели, как операции выполняют что-то в конвейере и возвращают поток. В итоге нам понадобится конечный результат. Здесь начинается работа терминала. Он не только дает нам желаемый результат, но и запускает Stream. Как я уже сказал, стрим ленив; он ничего не будет делать, независимо от того, сколько промежуточных операций мы добавляем в конвейер, если он не видит своего завершения, терминальной операции.
Вы не можете использовать тот же поток после того, как он был закрыт операцией терминала. В противном случае, это бросит
IllegalStateException: stream has already been operated upon or closed
. Вы должны начать новый поток
Терминальными операциями, которые мы использовали в приведенных выше примерах, являются forEach
и count
. Давайте рассмотрим их и несколько других ниже.
Foreach (Consumer)
forEach(Consumer)
принимает входные данные, но не возвращает выходные данные, принимая Consumer
в качестве параметра. Используйте это, если хотите что-то сделать, не возвращая ничего. Мы использовали его для печати номеров.
Джава
xxxxxxxxxx
1
list.stream()
2
// square of each number
3
.map(n -> n * n )
4
.forEach(n -> System.out.print(" "+ n));
5
//output:
6
// 1 4 9 16 25 36 49 64 81 100
кол-()
Как следует из названия, он используется для подсчета элементов, достигнутых до конца. Как в нашем примере фильтра, подсчет, кто выполнил все условия фильтра.
Джава
xxxxxxxxxx
1
list.stream()
2
// even numbers less than 5s
3
.filter(n -> n < 5 && n %2 == 0)
4
.count(); // returns 2
собирать ()
Я использую это чаще, чем любая другая терминальная операция. Позволяет создавать Collection
из элементов потока после их обработки. Это Collection
может быть List
, Set
или Map
. Коллекция включает элементы, прошедшие через последнюю трубу.
Джава
xxxxxxxxxx
1
List<Integer> processedList = list.stream()
2
// multiplying by 5
3
.map(n -> n * 5)
4
// numbers less than 30
5
.filter(n -> n < 30)
6
//add +2
7
.map(n -> n + 2)
8
// give the list of processed numbers
9
.collect(Collectors.toList());
10
System.out.println("processed List is \n " + processedList);
В конвейере мы умножили число на 5, затем отфильтровали числа меньше 30 и добавили 2. И после того, как все элементы завершат обработку, мы собираем их в список.
Джава
xxxxxxxxxx
1
processed List is
2
[7, 12, 17, 22, 27]
Заключение
Мы изучили, что такое Stream API, как его получить и как его лучше всего использовать. Этот пост похож на вводный пост для Stream API. В шахте так много алмазов, которые мы будем добывать в следующем посте.
Вы можете найти исходный код на GitHub или полный проект здесь .
Дальнейшее чтение
Ваше руководство по потокам Java [Учебники и статьи]
Обзор расширений API Java Stream
Что в имени: Соглашения об именах Java
Стать мастером серии Java Streams: часть 1 , 2 , 3 , 4 , 5 и 6