Вы находитесь на странице: 1из 39

Лекція №10 з дисципліни КПП (JAVA) на

тему:

Java Stream API


Java Stream API

Java Stream API обеспечивает функциональный подход к


обработке коллекций объектов. Java Stream API был добавлен
в Java 8 вместе с несколькими другими функциями
функционального программирования.
API Java поток не связан с Java InputStream и Java
OutputStream в Java IO . InputStream OutputStream связаны с
потоками байтов. Java Stream API предназначен для
обработки потоков объектов, а не байтов.
Java Stream API

Определение потока Java


Java Stream - это компонент, который может выполнять
внутреннюю итерацию своих элементов, то есть сам может
выполнять итерацию своих элементов. Напротив, когда вы
используете функции итерации Java Collections (например,
Java Iterator или цикл Java for-each, используемый с Java
Iterable ), вы должны реализовать итерацию элементов
самостоятельно.
Java Stream API
Обработка потока

Вы можете прикрепить слушателей к файлу Stream. Эти


слушатели вызываются при Stream внутренней итерации
элементов. Слушатели вызываются один раз для каждого
элемента в потоке. Таким образом, каждый слушатель
обрабатывает каждый элемент потока. Это называется
потоковой обработкой .

Слушатели потока образуют цепочку. Первый слушатель в


цепочке может обработать элемент в потоке, а затем вернуть
новый элемент для обработки следующему слушателю в
цепочке. Слушатель может либо вернуть тот же элемент, либо
новый, в зависимости от цели этого слушателя (процессора).
Java Stream API
Получить поток
Есть много способов получить Java Stream . Один из наиболее
распространенных способов получить Stream - из коллекции
Java . Вот пример получения Streamиз списка Java :

List <String> items = new ArrayList <String> ();

items.add («один»);
items.add («два»);
items.add («три»);
Stream <String> stream = items.stream ();
В этом примере сначала создается Java List, а затем к нему
добавляются три строки Java . Наконец, в примере
вызывается stream()метод для получения Stream экземпляра.
Java Stream API

Терминальные и нетерминальные операции

В Streamинтерфейсе есть выбор терминальных и


нетерминальных операций. Операция нетерминальный потока
представляет собой операцию , которая добавляет слушатель
к потоку , не делая ничего другого. Операции терминала поток
представляет собой операцию , которая запускает
внутреннюю итерацию элементов, вызывает все слушатели, и
возвращает результат.
Вот пример Java Stream, который содержит как
Java Stream API
нетерминальную, так и терминальную операцию:
public class StreamExamples {
public static void main(String[] args) {
List<String> stringList = new ArrayList<String>();
stringList.add("ONE");
stringList.add("TWO");
stringList.add("THREE");
Stream<String> stream = stringList.stream();
long count = stream
.map((value) -> { return value.toLowerCase(); })
.count();
System.out.println("count = " + count);
}
Java Stream API

Вызов map()метода Streamинтерфейса - это нетерминальная


операция. Он просто устанавливает лямбда-выражение в
потоке, которое преобразует каждый элемент в нижний
регистр. map()Более подробно этот метод будет рассмотрен
позже.

Вызов count()метода - это терминальная операция. Этот вызов


запускает внутреннюю итерацию, в результате которой
каждый элемент преобразуется в нижний регистр, а затем
подсчитывается.
Java Stream API

Нетерминальные операции

Нетерминальные потоковые операции Java Stream API - это


операции, которые преобразуют или фильтруют элементы в
потоке. Когда вы добавляете в поток нетерминальную
операцию, вы получаете в результате новый поток. Новый
поток представляет собой поток элементов, являющийся
результатом исходного потока с примененной нетерминальной
операцией. Вот пример нетерминальной операции,
добавленной к потоку, которая приводит к новому потоку:
Java Stream API

List<String> stringList = new ArrayList<String>();

stringList.add("ONE");
stringList.add("TWO");
stringList.add("THREE");

Stream<String> stream = stringList.stream();

Stream<String> stringStream =
stream.map((value) -> { return value.toLowerCase(); });
Java Stream API

Обратите внимание на stream map(). Этот вызов фактически


возвращает новый Stream экземпляр, представляющий
исходный поток строк с примененной операцией
сопоставления.
Вы можете добавить только одну операцию к данному
Streamэкземпляру. Если вам нужно связать несколько
операций друг за другом, вам нужно будет применить вторую
операцию к Streamоперации, полученной в результате первой
операции. Вот как это выглядит:
Stream<String> stringStream1 =
stream.map((value) -> { return value.toLowerCase(); });
Stream<String> stringStream2 =
stringStream1.map((value) -> { return
value.toUpperCase(); });
Java Stream API

Довольно распространено объединение вызовов с


нетерминальными операциями на Java Stream. Вот пример
цепочки вызовов нетерминальных операций в потоках Java:
Stream<String> stream1 = stream
.map((value) -> { return value.toLowerCase(); })
.map((value) -> { return value.toUpperCase(); })
.map((value) -> { return value.substring(0,3); });
Java Stream API

Многие нетерминальные операции Stream могут принимать в


качестве параметра лямбда-выражение Java . Это лямбда-
выражение реализует функциональный интерфейс Java,
который подходит для данной нетерминальной операции.
Например, интерфейс Functionor Predicate. Параметр
параметра метода нетерминальной операции обычно
является функциональным интерфейсом, поэтому он также
может быть реализован с помощью лямбда-выражения Java.
Java Stream API

filter()
Java Stream filter()можно использовать для фильтрации
элементов из Java Stream. filterМетод занимает ,
Predicateкоторый вызывается для каждого элемента в
потоке. Если элемент должен быть включен в результат
Stream, Predicateдолжен вернуться true. Если элемент не
должен быть включен, Predicateдолжен вернуться false.

Вот пример вызова Stream filter()метода Java :


Stream<String> longStringsStream = stream.filter((value) -> {
return value.length() >= 3;
});
Java Stream API

map()
Метод Java Stream map()преобразует (отображает) элемент в
другой объект. Например, если у вас есть список строк, он
может преобразовать каждую строку в нижний, верхний
регистр или в подстроку исходной строки или что-то
совершенно другое. Вот Stream map()пример Java :
List<String> list = new ArrayList<String>();
Stream<String> stream = list.stream();

Stream<String> streamMapped = stream.map((value) ->


value.toUpperCase());
Java Stream API
flatMap ()
Stream flatMap()Методы Java отображают один элемент на
несколько элементов. Идея состоит в том, что вы
«сглаживаете» каждый элемент от сложной структуры,
состоящей из нескольких внутренних элементов, до
«плоского» потока, состоящего только из этих внутренних
элементов.
Например, представьте, что у вас есть объект с вложенными
объектами (дочерними объектами). Затем вы можете
отобразить этот объект в «плоский» поток, состоящий из
самого себя плюс его вложенные объекты - или только
вложенные объекты. Вы также можете сопоставить поток
Listэлементов с самими элементами. Или сопоставьте поток
строк с потоком слов в этих строках - или с отдельными
Characterэкземплярами в этих строках.
Java Stream
Вот пример, который отображает плоское отображение
API
Listстрок на слова в каждой строке. Этот пример должен дать
вам представление о том, как flatMap()можно использовать
для отображения одного элемента на несколько элементов.
List<String> stringList = new ArrayList<String>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");
Stream<String> stream = stringList.stream();
stream.flatMap((value) -> {
String[] split = value.split(" ");
return (Stream<String>) Arrays.asList(split).stream();
})
.forEach((value) -> System.out.println(value))
Java Stream API

Вызываемая flatMap()операция Streamдолжна возвращать


другую, Streamпредставляющую плоские отображенные
элементы. В приведенном выше примере каждая исходная
строка разбивается на слова, превращается в List, а поток
получается и возвращается из них List.

Обратите внимание, что этот пример заканчивается вызовом,


forEach()который является операцией терминала. Этот вызов
нужен только для запуска внутренней итерации и,
следовательно, операции с плоской картой. Если бы в
Streamцепочке не вызывалась терминальная операция ,
ничего бы не произошло. На самом деле плоского
картографирования не было бы.
Java Stream API

distinct()
Метод Java Stream distinct()- это нетерминальная операция, которая возвращает новую,
Streamкоторая будет содержать только отдельные элементы из исходного потока. Любые
дубликаты будут удалены. Вот пример Stream distinct()метода Java :
List<String> stringList = new ArrayList<String>();

stringList.add("one");
stringList.add("two");
stringList.add("three");
stringList.add("one");

Stream<String> stream = stringList.stream();

List<String> distinctStrings = stream


.distinct()
.collect(Collectors.toList());

System.out.println(distinctStrings);
limit()
Java Stream API
Метод Java Stream limit()может ограничить количество
элементов в потоке числом, заданным limit()методу в качестве
параметра. limit()Метод возвращает новый , Streamкоторый
будет максимально содержать заданное количество
элементов. Вот Stream limit()пример Java :
List<String> stringList = new ArrayList<String>();
stringList.add("one");
stringList.add("two");
stringList.add("three");
stringList.add("one");
Stream<String> stream = stringList.stream();
stream
.limit(2)
peek() Java Stream API
Метод Java Stream peek()- это нетерминальная операция, которая принимает в качестве
параметра Consumer( java.util.function.Consumer). ConsumerБудет вызвана для каждого элемента
в потоке. peek()Метод возвращает новый , Streamкоторый содержит все элементы в исходном
потоке.

Как peek()говорится в методе, целью метода является просмотр элементов в потоке, а не их


преобразование. Имейте в виду, что peekметод не запускает внутреннюю итерацию элементов в
потоке. Для этого вам нужно вызвать терминальную операцию. Вот Stream peek()пример Java :
List<String> stringList = new ArrayList<String>();

stringList.add("abc");
stringList.add("def");

Stream<String> stream = stringList.stream();

Stream<String> streamPeeked = stream.peek((value) -> {


System.out.println("value");
});
Терминальные операции
Java Stream API

Терминальные операции Streamинтерфейса Java обычно


возвращают одно значение. Как только операция терминала
вызывается для a Stream, начинается итерация Streamпотока
и любого из связанных потоков. После завершения итерации
возвращается результат работы терминала.

Терминальная операция обычно не возвращает новый


Streamэкземпляр. Таким образом, как только вы вызываете
терминальную операцию в потоке, цепочка
Streamэкземпляров из нетерминальной операции
заканчивается. Вот пример вызова терминальной операции на
Java Stream:
Java Stream API

long count = stream


.map((value) -> { return value.toLowerCase(); })
.map((value) -> { return value.toUpperCase(); })
.map((value) -> { return value.substring(0,3); })
.count();
Это вызов count()в конце примера, который является
операцией терминала. Поскольку count()возвращает a long,
Streamцепочка нетерминальных операций ( map()вызовов)
завершается.
Java Stream API

anyMatch ()

Метод Java Stream anyMatch()- это терминальная операция, которая принимает один
Predicateпараметр as, запускает внутреннюю итерацию Streamи применяет Predicateпараметр к
каждому элементу. Если Predicateдля любого из элементов возвращается значение true,
anyMatch()метод возвращается true. Если ни один элемент не соответствует Predicate,
anyMatch()вернется false. Вот Stream anyMatch()пример Java :
List<String> stringList = new ArrayList<String>();

stringList.add("One flew over the cuckoo's nest");


stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");

Stream<String> stream = stringList.stream();

boolean anyMatch = stream.anyMatch((value) -> { return value.startsWith("One"); });


System.out.println(anyMatch);
Java Stream API
allMatch ()

Метод Java Stream allMatch()- это терминальная операция, которая принимает один
Predicateпараметр as, запускает внутреннюю итерацию элементов в Streamи применяет
Predicateпараметр к каждому элементу. Если Predicateвозвращается trueдля всех элементов в
Stream, allMatch()вернется true. Если не все элементы соответствуют Predicate, allMatch()метод
возвращается false. Вот Stream allMatch()пример Java :
List<String> stringList = new ArrayList<String>();

stringList.add("One flew over the cuckoo's nest");


stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");

Stream<String> stream = stringList.stream();

boolean allMatch = stream.allMatch((value) -> { return value.startsWith("One"); });


System.out.println(allMatch);
Java Stream API
noneMatch ()

Метод Java Stream noneMatch()- это терминальная операция, которая будет перебирать
элементы в потоке и возвращать trueили false, в зависимости от того, не совпадают ли элементы
в потоке с Predicateпереданным noneMatch()параметром as. noneMatch()Метод возвращает ,
trueесли ни один из элементов не будут сопровождаться Predicate, и , falseесли один или
несколько элементов совпадают. Вот Stream noneMatch()пример Java :
List<String> stringList = new ArrayList<String>();

stringList.add("abc");
stringList.add("def");

Stream<String> stream = stringList.stream();

boolean noneMatch = stream.noneMatch((element) -> {


return "xyz".equals(element);
});

System.out.println("noneMatch = " + noneMatch);


Java Stream API

collect()
Метод Java Stream collect()- это терминальная операция, которая запускает внутреннюю
итерацию элементов и собирает элементы в потоке в коллекцию или какой-либо объект. Вот
простой Stream collect()пример метода Java :

List<String> stringList = new ArrayList<String>();

stringList.add("One flew over the cuckoo's nest");


stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");

Stream<String> stream = stringList.stream();

List<String> stringsAsUppercaseList = stream


.map(value -> value.toUpperCase())
.collect(Collectors.toList());
Java Stream API

collect()Метод принимает Collector( java.util.stream.Collector) в


качестве параметра. Реализация Collectorтребует некоторого
изучения Collectorинтерфейса. К счастью, класс Java
java.util.stream.Collectorsсодержит набор предварительно
реализованных Collectorреализаций, которые вы можете
использовать для наиболее распространенных операций. В
приведенном выше примере Collectorиспользовалась
возвращенная им реализация Collectors.toList(). Это
Collectorпросто собирает все элементы в потоке в
стандартном JavaList
Java Stream API

count()
Метод Java Stream count()- это терминальная операция, которая запускает внутреннюю итерацию
элементов в Streamи подсчитывает количество элементов. Вот Stream count()пример Java :
List<String> stringList = new ArrayList<String>();

stringList.add("One flew over the cuckoo's nest");


stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");

Stream<String> stream = stringList.stream();

long count = stream.flatMap((value) -> {


String[] split = value.split(" ");
return (Stream<String>) Arrays.asList(split).stream();
})
.count();

System.out.println("count = " + count);


Java Stream API

findFirst ()

Метод Java Stream findFirst()находит первый элемент в


Stream, если какие-либо элементы присутствуют в Stream.
findFirst()Метод возвращает , Optionalиз которого можно
получить элемент, если таковой имеется.
findAny ()

Метод Java Stream findAny()может найти единственный


элемент из Stream. Найденный элемент может быть из любого
места в Stream. Нет никакой гарантии относительно того,
откуда в потоке был взят элемент.
Java Stream API

forEach
Метод Java Stream forEach()- это терминальная операция,
которая запускает внутреннюю итерацию элементов в Streamи
применяет Consumer( java.util.function.Consumer) к каждому
элементу в Stream. В forEach()метод возвращает void. Вот
Stream forEach()пример Java :
List<String> stringList = new ArrayList<String>();
stringList.add("one");
stringList.add("two");
stringList.add("three");
stringList.add("one");
Stream<String> stream = stringList.stream();
stream.forEach( element -> { System.out.println(element); });
Java Stream API

min()
Метод Java Stream min()- это терминальная операция, которая возвращает наименьший элемент
в Stream. Какой элемент самый маленький, определяется Comparatorреализацией, которую вы
передаете min()методу. Я объяснил, как Comparatorработает интерфейс, в моем руководстве по
сортировке коллекций Java . Вот Stream min()пример Java :
List<String> stringList = new ArrayList<String>();

stringList.add("abc");
stringList.add("def");

Stream<String> stream = stringList.stream();

Optional<String> min = stream.min((val1, val2) -> {


return val1.compareTo(val2);
});
String minString = min.get();
System.out.println(minString);
Java Stream API

max()
Метод Java Stream max()- это терминальная операция, которая возвращает самый большой
элемент в Stream. Какой элемент является самым большим, определяется
Comparatorреализацией, которую вы передаете max()методу. Я объяснил, как
Comparatorработает интерфейс, в моем руководстве по сортировке коллекций Java . Вот Stream
max()пример Java :
List<String> stringList = new ArrayList<String>();

stringList.add("abc");
stringList.add("def");

Stream<String> stream = stringList.stream();

Optional<String> max = stream.max((val1, val2) -> {


return val1.compareTo(val2);
});

String maxString = max.get();


Java Stream API

reduce()
Метод Java Stream reduce()- это терминальная операция, которая может сократить все элементы
в потоке до одного элемента. Вот Stream reduce()пример Java :
List<String> stringList = new ArrayList<String>();

stringList.add("One flew over the cuckoo's nest");


stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");

Stream<String> stream = stringList.stream();

Optional<String> reduced = stream.reduce((value, combinedValue) -> {


return combinedValue + " + " + value;
});

System.out.println(reduced.get());
Java Stream API

toArray()
Метод Java Stream toArray()- это терминальная операция,
которая запускает внутреннюю итерацию элементов в потоке
и возвращает массив, Objectсодержащий все элементы. Вот
Stream toArray()пример Java :
List<String> stringList = new ArrayList<String>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");
Stream<String> stream = stringList.stream();
Object[] objects = stream.toArray();
Concatenate Streams
Java Stream API
Интерфейс Java Streamсодержит статический метод, называемый, concat()который может
объединять два потока в один. Результатом является новый, Streamсодержащий все элементы из
первого потока, за которым следуют все элементы из второго потока. Вот пример использования
Stream concat()метода Java :
List<String> stringList = new ArrayList<String>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");

Stream<String> stream1 = stringList.stream();


List<String> stringList2 = new ArrayList<>();
stringList2.add("Lord of the Rings");
stringList2.add("Planet of the Rats");
stringList2.add("Phantom Menace");
Stream<String> stream2 = stringList2.stream();
Stream<String> concatStream = Stream.concat(stream1, stream2);
List<String> stringsAsUppercaseList = concatStream
.collect(Collectors.toList());
System.out.println(stringsAsUppercaseList);
Создать поток из массива
Java Stream API
Интерфейс Java Streamсодержит вызываемый статический
метод, of()который можно использовать для создания
Streamиз одного или нескольких объектов. Вот пример
использования метода Java Stream of():

Stream<String> streamOf = Stream.of("one", "two", "three");


sorted
Начнем с операции sorted () - она ​сортирует элементы потока на
основе переданного компаратора.
Например, мы можем отсортировать сотрудников по их именам:
List<Employee> employees = empList.stream()
.sorted((e1, e2) -> e1.getName().compareTo(e2.getName()))
.collect(Collectors.toList());
Java Stream API

Расширенный сбор
joining

Мы уже видели, как мы использовали Collectors.toList () для извлечения списка из потока. Давайте
теперь посмотрим еще несколько способов сбора элементов из потока.
String empNames = empList.stream()
.map(Employee::getName)
.collect(Collectors.joining(", "))
.toString();
toSet

Set<String> empNames = empList.stream()


.map(Employee::getName)
.collect(Collectors.toSet());
Java Stream API

toCollection
Vector<String> empNames = empList.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(Vector::new));
groupingBy
groupingBy () предлагает расширенное разделение - где мы можем разделить поток более чем
на две группы.

Он принимает в качестве параметра функцию классификации. Эта функция классификации


применяется к каждому элементу потока.

Значение, возвращаемое функцией, используется как ключ к карте, которую мы получаем от


сборщика groupingBy :
Map<Character, List<Employee>> groupByAlphabet = empList.stream().collect(
Collectors.groupingBy(e -> new Character(e.getName().charAt(0))));

Вам также может понравиться