Java 8 была выпущена в 2014 году, принеся с собой множество новых функций, которые современные разработчики теперь считают необходимыми, таких как лямбда-выражение, улучшения API параллелизма, функциональные интерфейсы и улучшения для обработки массовых данных. Хотя с тех пор прошло много лет, Java 8 остается наиболее используемой версией Java: более 60% профессиональных разработчиков в 2020 году сообщили, что Java 8 является их стандартной версией office.
Процент разработчиков, сообщивших, что в качестве основного приложения они указали версию Java:
В результате, независимо от того, меняете ли вы работу или только начинаете, владение Java 8 является необходимым навыком в современном мире технологий. Если вы только сейчас переключаете версии Java, вам может показаться, что вы немного опоздали на вечеринку, но не волнуйтесь! Функции Java 8 освоить проще, чем вы думаете, и сегодня я познакомлю вас с одним из самых важных обновлений Java 8: Stream API.
Сегодня мы рассмотрим:
- Что такое поток в Java 8?
- Особенности потока
- Сравнение потоков и циклов
- Конвейер Java 8 Stream API: Промежуточные и конечные операции
- Чему вы должны научиться дальше?
Освоите Java 8 с практическим опытом
Изучайте все мощные функции Java 8 на практике и в своем собственном темпе, без необходимости перезапуска с нуля.
Java 8 для опытных разработчиков: Лямбды, потоковый API и многое другое
Что такое поток в Java 8?
По словам всемогущего Oracle Interface Package Summary, поток – это “последовательность элементов, поддерживающих последовательные и параллельные агрегированные операции”. Несмотря на то, что это отличная скороговорка, это не самое удобоваримое определение. Позвольте мне перевести.
Потоки – это абстрактный слой, добавленный в Java 8, который позволяет разработчикам легко манипулировать коллекциями объектов или примитивов. Это не структура данных, поскольку в ней не хранятся данные; скорее, она служит средством преобразования от источника данных к месту назначения.
Агрегатные операции , иногда называемые потоковыми операциями, – это просто операции, уникальные для потоков, которые мы можем использовать для преобразования части (или всего) потока одновременно. Мы увидим примеры этого позже.
Наконец, последовательный против параллельного относится к возможности реализации параллелизма при выполнении потоковых операций. Операция может быть либо применена по одному одному потоку, либо она может быть разделена между несколькими потоками, каждый из которых применяет операцию одновременно.
Поток часто визуализируется как конвейер , поскольку он действует как промежуточный этап между источником данных, каким-то образом преобразует его, а затем выводит его в новой форме ниже по потоку. Мы вернемся к этой метафоре, когда рассмотрим Промежуточные и конечные операции ниже.
Использование Stream API требует принципиально иного стиля кодирования, чем традиционное кодирование – в то время как большая часть кода на Java написана в императивном стиле, где разработчик инструктирует, что делать и как это выполнить, операции из Stream API требуют декларативного или управляемого событиями стиля, аналогичного операциям в SQL .
Особенности потока
Теперь, когда мы знаем, что такое поток, вот краткий обзор некоторых качеств, которыми вы можете воспользоваться при использовании потока.
- Созданный с использованием исходной коллекции или массива
- Может преобразовать группу, но не может изменить данные внутри
- Легко позволяет манипулировать целыми коллекциями одновременно
- Потоки могут обрабатываться декларативно
- Ни хранит данные, ни настраивает данные, которые он обрабатывает, поэтому он не является структурой данных.
- Настраивается с помощью лямбда-выражений
Сравнение потоков и циклов
Потоки часто сравнивают с циклами, поскольку и те, и другие используются для создания итеративного поведения в программе. По сравнению с циклами потоки выглядят намного чище в строке из-за сокращения загроможденного синтаксиса цикла. Потоки, возможно, легче понять с первого взгляда благодаря их декларативному стилю.
Ниже у нас есть два фрагмента кода Java, которые оба выполняют одну и ту же задачу печати набора данных, stream
с помощью streams while loop
с циклом for. Взгляните, чтобы увидеть, чем они отличаются! Мы разберем это немного подробнее ниже.
течение
import java.util.stream.*; class StreamDemo { public static void main(String[] args) { Streamstream = Stream.of(1,2,3,4,5,6,7,8,9); stream.forEach(p -> System.out.println(p)); } }
петля
class ArrayTraverse { public static void main(String args[]) { int my_array[]={1,2,3,4,5,6,7,8,9}; for(int i:my_array) { System.out.print(i+" "); } } }
Обратите внимание, что когда мы используем операцию автоматической итерации forEach()
stream, мы можем сократить строки и сделать наш код более читаемым.
В цикле for большая часть кода посвящена созданию итерации, а не печати массива. Это добавляет много дополнительного времени разработчику. Поскольку потоковые операции библиотеки API обрабатывают итерации, ваш код может просто выражать логику вычислений вместо того, чтобы управлять каждым маленьким байтом потока управления.
Недостатком принятия этого стиля, однако, является то, что переход может быть трудным для ветеранов отрасли, которые годами использовали императивный стиль. Если вы изо всех сил пытаетесь поднять его, вы не одиноки!
Другим недостатком, который следует отметить, является то, что потоки, особенно параллельные потоки, требуют значительно больших накладных расходов, чем цикл for. Имейте это в виду, если накладные расходы являются проблемой в вашей программе.
Преимущества и недостатки потока
Преимущества:
- Меньше визуального беспорядка в коде
- Нет необходимости писать итератор
- Может написать “что”, а не “как”, понятное с первого взгляда
- Выполняется так же быстро, как циклы for (или быстрее при параллельных операциях)
- Отлично подходит для больших списков
Недостатки:
- Большие накладные расходы
- Излишество для небольших коллекций
- Трудно уловить, если привыкнуть к традиционному кодированию в императивном стиле
Продолжайте учиться.
Не останавливайтесь только на потоках. Изучите более продвинутые концепции Java, такие как лямбда-выражения Java 8, Java Time и многое другое, с помощью интерактивных примеров кодирования. Попрощайтесь с Hello World и привет урокам и учебным пособиям, адаптированным к вашему уровню квалификации!
Java 8 для опытных разработчиков: Лямбды, Stream API и многое другое
Конвейер Java 8 Stream API: Промежуточные и конечные операции
Агрегированные операции бывают двух типов: промежуточные и терминальные. Каждый поток имеет ноль или более промежуточных операций и одну терминальную операцию, а также источник данных в самой дальней точке вверх по потоку, такой как массив или список.
Промежуточные операции принимают поток в качестве входных данных и возвращают поток после завершения, что означает, что несколько операций могут быть выполнены подряд.
В нашей предыдущей метафоре эти операции подобны сегментам трубы, в которые вводятся и выводятся данные, подобные воде, без прерывания потока. Хотя они могут перенаправить поток в новом направлении или изменить его форму, он все равно может течь. Общие примеры промежуточной операции air filter()
, for Each()
и map()
. Давайте обсудим их ниже.
фильтр()
Этот метод принимает поток и выбирает его часть на основе переданных критериев. В нашей метафоре filter()
будет либо соединением трубы, либо клапаном, делегирующим часть потока для прохождения отдельным путем. Например, ниже мы используем filter()
для потока целых чисел, чтобы вернуть поток с целыми числами больше 10.
import java.util.ArrayList; import java.util.List; import java.util.stream.*; class StreamDemo { public static void main(String[] args) { //Created a list of integers Listlist = new ArrayList<>(); list.add(1); list.add(12); list.add(23); list.add(45); list.add(6); list.stream() // Created a stream from the list .filter(num -> num > 10) //filter operation to get only numbers greater than 10 .forEach(System.out::println); // Printing each number in the list after filtering. //Again printing the elements of List to show that the original list is not modified. System.out.println("Original list is not modified"); list.stream() .forEach(System.out::println); } }
Важно отметить, что это не изменяет исходный поток, что делает его эффективным для задач поиска со статическими данными, таких как поиск в базе данных.
карта()
Этот метод принимает поток и другой метод в качестве входных данных, применяя эту функцию к каждому элементу в потоке. Эти результаты затем используются для заполнения нового потока, который отправляется вниз по течению.
Например, ниже мы используем map()
для потока имен, чтобы применить метод toUpperCase
к каждому имени. Это создает новый поток с именем каждого человека, написанным с заглавной буквы. Это только один тип операции с картой. Другими являются mapToInt()
, который преобразует поток в целые числа, и flatMap()
, который объединяет все элементы потока вместе.
import java.util.ArrayList; import java.util.List; import java.util.stream.*; class StreamDemo { public static void main(String[] args) { Listlist = new ArrayList<>(); list.add("Dave"); list.add("Joe"); list.add("Ryan"); list.add("Iyan"); list.add("Ray"); // map() is used to convert each name to upper case. // Note: The map() method does not modify the original list. list.stream() .map(name -> name.toUpperCase()) //map() takes an input of Function type. .forEach(System.out::println); // forEach() takes an input of Consumer type. } }
Терминальные операции возвращают что-то отличное от потока, например примитив или объект. Это означает, что, хотя многие промежуточные операции могут выполняться последовательно, может быть только одна терминальная операция. В нашей метафоре эти операции визуализируются как конец строки; поток поступает, но его поток останавливается, оставляя данные другого типа. Хотя эти данные можно было бы вернуть в потоковую форму, это был бы не тот же поток, что и входные данные из нашей терминальной операции.
Наиболее распространенным примером терминальной операции является функция forEach()
.
forEach()
Этот метод принимает поток в качестве входных данных, выполняет итерацию по потоку, выполняет действие над каждым элементом внутри и выводит результат этого действия. Ключевое различие между forEach() |/и |/map()
заключается в том, что первый возвращает данные в непотоковой форме, что делает его терминальным.
Давайте еще раз посмотрим на наш предыдущий пример. Теперь мы можем отметить, что выходные данные представляют собой группу индивидуально напечатанных целых чисел, а не поток собранных целых чисел.
import java.util.stream.*; class StreamDemo { public static void main(String[] args) { Streamstream = Stream.of(1,2,3,4,5,6,7,8,9); stream.forEach(p -> System.out.println(p)); } }
Чему вы должны научиться дальше?
Команды разработчиков по всей отрасли ясно дали это понять, Java 8 пользуется большой популярностью, и она останется здесь навсегда. Если вы находитесь на пути к освоению Java 8, это только начало, и вам предстоит изучить так много интересных функций.
Чтобы помочь вам в этом путешествии, мы рады предложить Java 8 для опытных разработчиков: Lambdas, Stream API & Beyond , курс, написанный опытным разработчиком Java Сауравом Аггарвалом, наполненный советами и интерактивными примерами по таким темам, как расширение ваших потоков с помощью лямбда-выражений, глубокое погружение в расширенный Stream API, управление коллекциями, завершаемый будущий параллелизм и многое другое.
Куда бы вы ни отправились дальше, я желаю вам удачи в продолжении вашего путешествия по Java 8!
Продолжайте читать о Java
- 15 Вопросов для собеседования на Java, которые помогут вам подготовиться
- Почему Java более безопасен, чем другие языки
Оригинал: “https://dev.to/educative/java-8-tutorial-master-stream-api-and-beyond-1p9b”