Стримы.JAVA Flashcards
Что такое Stream API? Для чего нужны стримы?
Интерфейс java.util.Stream представляет собой последовательность элементов, над которой можно производить различные операции.
Нужны для упрощения работы с наборами данных, в частности, упростить операции фильтрации, сортировки и другие манипуляции с данными.
IntStream.of(50, 60, 70, 80, 90, 100, 110, 120).filter(x -> x < 90).map(x -> x + 10).limit(3).forEach(System.out::print);
Cоздаем экземпляр Stream
Пустой стрим: Stream.empty()
Стрим из List: list.stream()
Стрим из Map: map.entrySet().stream()
Стрим из массива: Arrays.stream(array)
Стрим из указанных элементов: Stream.of("1", "2", "3")
Стрим из BufferedReader с помощью метода lines (); нужно закрывать close ().
Промежуточные (“intermediate”, “lazy”) — обрабатывают поступающие элементы и возвращают стрим. Может быть много, а может и не быть ни одной.
Терминальные (“terminal”, ещё называют “eager”) — обрабатывают элементы и завершают работу стрима, может быть только один.
Важные моменты:
Обработка не начнётся до тех пор, пока не будет вызван терминальный оператор. list.stream().filter(s -> s > 5) (не возьмёт ни единого элемента из списка);
Экземпляр стрима нельзя использовать более одного раза;
Кроме универсальных объектных существуют особые виды стримов для работы с примитивными типами данных int, long и double: IntStream, LongStream и DoubleStream. Эти примитивные стримыработают так же, как и обычные объектные, но со следующими отличиями:
используют специализированные лямбда-выражения, например, IntFunction или IntPredicate вместо Function и Predicate;
поддерживают дополнительные конечные операции sum(), average(), mapToObj()
Почему Stream называют ленивым?
Ленивое программирование - технология, которая позволяет вам отсрочить вычисление кода до тех пор, пока не понадобится его результирующее значение.
Блок обработки – промежуточные операции не выполняются, пока не вызовется терминальная.
Stream в Java называют ленивым потоком (lazy stream), потому что он не выполняет операции на элементах коллекции сразу, а откладывает их до момента, когда они станут необходимы для вычисления конечного результата.
Это означает, что когда мы вызываем методы filter(), map() или reduce() на потоке, они не выполняются сразу на каждом элементе коллекции, а лишь создают новый поток с операциями, которые нужно выполнить. Таким образом, поток можно рассматривать как последовательность задач, которые будут выполнены только тогда, когда это станет необходимым.
Преимущество ленивости Stream заключается в том, что это позволяет оптимизировать вычисления и экономить ресурсы, особенно в случаях, когда не все элементы коллекции необходимы для вычисления конечного результата. Например, если мы фильтруем коллекцию, чтобы получить только первые 10 элементов, Stream будет выполнять операции только на первых 10 элементах коллекции, а не на всех.
Кроме того, ленивость Stream позволяет использовать его с бесконечными коллекциями данных, такими как поток случайных чисел или последовательность Фибоначчи. В таких случаях Stream выполняет операции только на том количестве элементов, которое необходимо для вычисления конечного результата, не загружая память всей коллекции данных.
В целом, ленивость Stream позволяет упростить и оптимизировать обработку данных, особенно при работе с большими коллекциями или потоками данных.
Какие существуют способы создания стрима?
Из коллекции: Stream<String> fromCollection = Arrays.asList("x", "y", "z").stream();
Из набора значений: Stream<String> fromValues = Stream.of("x", "y", "z");
Из массива: Stream<String> fromArray = Arrays.stream(new String[]{"x", "y", "z"});
Из файла (каждая строка в файле будет отдельным элементом в стриме): Stream<String> fromFile = Files.lines(Paths.get("input.txt"));
Из строки: IntStream fromString = "0123456789".chars();
С помощью Stream.builder(): Stream<String> fromBuilder = Stream.builder().add("z").add("y").add("z").build();
С помощью Stream.iterate() (бесконечный): Stream<Integer> fromIterate = Stream.iterate(1, n -> n + 1);
С помощью Stream.generate() (бесконечный): Stream<String> fromGenerate = Stream.generate(() -> "0");</String></Integer></String></String></String></String></String>
Как из коллекции создать стрим?
Stream<String> fromCollection = Arrays.asList("x", "y", "z").stream();</String>
Какие промежуточные методы в стримах вы знаете?
concat(Stream<? extends T> a, Stream<? extends T> b): объединяет два потока.
distinct(): возвращает поток, в котором имеются только уникальные данные с типом T.
dropWhile(Predicate<? super T> predicate): пропускает элементы, которые соответствуют условию в predicate, пока не попадется элемент, который не соответствует условию. Выбранные элементы возвращаются в виде потока.
filter(Predicate<? super T> predicate): фильтрует элементы в соответствии с условием в предикате.
limit(long maxSize): оставляет в потоке только maxSize элементов.
map(Function<? super T,? extends R> mapper): преобразует элементы типа T в элементы типа R и возвращает поток с элементами R.
flatMap(Function<? super T, ? extends Stream<? extends R» mapper): позволяет преобразовать элемент типа T в несколько элементов типа R и возвращает поток с элементами R.
skip(long n): возвращает поток, в котором отсутствуют первые n элементов.
sorted(): возвращает отсортированный поток.
sorted(Comparator<? super T> comparator): возвращает отсортированный в соответствии с компаратором поток.
takeWhile(Predicate<? super T> predicate): выбирает из потока элементы, пока они соответствуют условию в predicate. Выбранные элементы возвращаются в виде потока.
Расскажите про метод peek() быстрый взгляд.
Stream<T> peek(Consumer<? super T> action);</T>
peek() - возвращает стрим, дополнительно выполняя указанное действие над каждым элементом.
integerStream.peek(System.out::println) - Позволяет подсмотреть какие элементы летают на данном этапе с помощью System.out::println
Расскажите про метод map() маппинг из одного в другое
Отображение или маппинг позволяет задать функцию преобразования одного объекта в другой, то есть получить из элемента одного типа элемент другого типа.
принимает Function.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
map(n -> n.toString())
</R></R>
Расскажите про метод flatMap(). плоский маппинг
Плоское отображение выполняется тогда, когда из одного элемента нужно получить несколько.
Stream
.of("H e l l o", "w o r l d !") .flatMap((p) -> Arrays.stream(p.split(" "))) .toArray(String[]::new);//["H", "e", "l", "l", "o", "w", "o", "r", "l", "d", "!"]
Например, в примере выше мы выводим название телефона и его цену. Но что, если мы хотим установить для каждого телефона цену со скидкой и цену без скидки. То есть из одного объекта Phone нам надо получить два объекта с информацией, например, в виде строки. Дляэтого применим flatMap:
Stream<Phone> phoneStream = Stream.of(new Phone("iPhone 6 S", 54000), new Phone("Lumia 950", 45000),new Phone("Samsung Galaxy S 6", 40000));</Phone>
phoneStream
.flatMap(p->Stream.of(String.format("название: %s цена без скидки: %d", p.getName(), p.getPrice()), String.format("название: %s цена со скидкой: %d", p.getName(), p.getPrice()-(int)(p.getPrice()*0.1)))) .forEach(s->System.out.println(s));
Чем отличаются методы map() и flatMap().
Разница заключается в том, что операция map() создает одно выходное значение для каждого входного значения, тогда как операция flatMap() создает произвольное число (ноль или больше) значений для каждого входного значения.
Расскажите про метод filter()
Метод filter() является промежуточной операцией принимающей предикат, который фильтрует все элементы, возвращая только те, что соответствуют условию.
Stream<String> citiesStream = Stream.of("Париж", "Лондон", "Мадрид","Берлин", "Брюссель");</String>
citiesStream.filter(s->s.length()==6).forEach(s->System.out.println(s));
чтобы отбросить null с потока данных .filter(Objects::nonNull)
Расскажите про метод limit()
limit(long n) применяется для выборки первых n элементов потоков. Этот метод также возвращает модифицированный поток, в котором не более n элементов.
Stream<String> phoneStream = Stream.of("iPhone 6 S", "Lumia 950", "Samsung Galaxy S 6", "LG G 4","Nexus 7");</String>
phoneStream.skip(1)
.limit(2) .forEach(s->System.out.println(s)); // Lumia 950 Samsung Galaxy S 6
Расскажите про метод skip()
skip(long n) используется для пропуска n элементов. Этот метод возвращает новый поток, в которомпропущены первые n элементов.
Расскажите про метод sorted()
Для простой сортировки по возрастанию применяется метод sorted(). Подходит только для сортировки тех объектов, которые реализуют интерфейс Comparable.
Если же у нас классы объектов не реализуют этот интерфейс или мы хотим создать какую-то свою логику сортировки, то мы можем использовать другую версию метода sorted(), которая в качестве параметра принимает компаратор.
class Phone{
private String name; private String company; private int price; public Phone(String name,
String comp, int price){
this.name=name; this.company=comp; this.price = price; } public String getName() {
return name; }
public int getPrice() {
return price; }
public String getCompany() {
return company; }
}
import java.util.Comparator;
import java.util.stream.Stream;
public class Program {
public static void main(String[] args) { Stream<Phone> phoneStream = Stream.of(new Phone(\"iPhone
X", "Apple", 600),
new Phone(\"Pixel 2\", \"Google\", 500), new Phone(\"iPhone 8\", \"Apple\",450), new Phone(\"Nokia 9\", \"HMD Global\",150), new Phone(\"Galaxy S9\", \"Samsung\", 300)); phoneStream.sorted(new PhoneComparator()) .forEach(p->System.out.printf(\"\%\s (\%\s) - \%\d \n\", p.getName(), p.getCompany(), p.getPrice())); }
}
class PhoneComparator implements Comparator<Phone>{</Phone>
public int compare(Phone a, Phone b){ return
a.getName().toUpperCase().compareTo(b.getName().toUpperCase());
}
}
Расскажите про метод distinct()
“особый” возвращает только ункальные элементы в виде потока
Stream<String> people = Stream.of(\"Tom\", \"Bob\", \"Sam\", \"Tom\", \"Alice\", \"Kate\", \"Sam\");</String>
people.distinct().forEach(p -> System.out.println(p));
Какие терминальные методы в стримах вы знаете?
Терминальные методы (“terminal”) - методы обрабатывающие элементы потока и
завершающие его работу. Терминальный метод в потоке может быть только один. Именно
вызов терминального метода и запускает процесс выполнения цепочки промежуточных
методов.
forEach (принимает Consumer) например System.out::println (покажет все элементы, которые остались в стриме)
Optional<T> findAny() Вернет Optional если элемент в потоке есть, или пустой если нет
Optional<T> findFirst() Вернет Optional с первым элементом в потоке есть, или пустой если нет
allMatch (принимает предикат) позволяет удостовериться, удовлетворяют ли все элементы стрима определенному условию
min () возвращает минимальный элемент из стрима
count () возвращает количество элементов, оставшееся в стриме
sum () 0
collect () собирает элементы стрима в новое хранилище, например список. Куда может собрать смотри список Collectors:
groupingBy группирует по параметрам
averagingInt среднеарифметическое какого то параметра
summarizingInt дает кол-во элементов, суммму, мин, ср.арифм, макс значения
reduce () – позволяет вычислить свертку элементов стрима (результат применения некоторого бинарного оператора к паре элементов из стрима, пока от стрима не останется один единсттвенный элемент) свёртка — это математическая операция, применённая к двум функциям f и g, порождающая третью функцию, которая иногда может рассматриваться как модифицированная версия одной из первоначальных
Optional<T> max(Comparator<? super T> comparator) Вернет максимальный элемент из потока данных
Optional<T> min(Comparator<? super T> comparator) Вернет минимальный элемент из потока данных</T></T></T></T>