Стримы java. Стримы. 1 Функциональные интерфейсы, основные типы
Скачать 149.5 Kb.
|
1) Функциональные интерфейсы, основные типы. Интерфейс называется функциональным если у него есть ровно один абстрактный метод проверить это можно аннатацией @Functinalinterface 1) Consumer - потребители, те кто принимают но не возвращают в замен. Его подвиды IntConsumer, LongConsumer, DoubleConsumer они есть потому, что дженерики не могут парметризоваться примитивами. 2) Supplir - поставщики, они не принимают ни какое значение, а просто возращают как оето значение. 3) Predicate - приниает выражение какого то типа а наружу выдает булевское значение. есть для примитивов, есть булевские 4) Function - принимает аргумент и возвращает значение какого то типа, в общем случаее эти типы разные. Бывает принимают два параметра. могут стоять примитивы 5) Operator - это частный случай функции, когда на вход подается значение одного и того же типа. Унарный оператор принимает один параметр а Бинарный два параметра отдельные интерфейсы над лонгами и даблами 2) Что такое лямбда выражение и ссылка на метод. Как они связаны с анонимным классом Лямбда это возможность замены вызова реализации интерфейса с одним методом. Лямбдой вырожением мы просто более компактно записываем, вызов метода функционального интерфейса с одним методом Лямбда представляет набор инструкций, которые можно выделить в отдельную переменную и затем многократно вызвать в различных местах программы. Основу лямбда-выражения составляет лямбда-оператор, который представляет стрелку ->. Этот оператор разделяет лямбда-выражение на две части: левая часть содержит список параметров выражения, а правая собственно представляет тело лямбда-выражения, где выполняются все действия. Лямбда-выражение, образует реализацию метода, определенного в функциональном интерфейсе. По факту лямбда-выражения являются в некотором роде сокращенной формой внутренних анонимных классов, которые ранее применялись в Java. Если существующий в классе метод уже делает все, что необходимо, то можно воспользоваться механизмом method reference (ссылка на метод) для непосредственной передачи этого метода. на 1) статический метод; имя_класса::имя_статического_метода для статического метода; 2) на метод экземпляра; объект_класса::имя_метода для метода экземпляра; 3) на конструкторе. название_класса::new для конструктора. Ссылки на методы потенциально более эффективны, чем использование лямбда-выражений. Кроме того, они предоставляют компилятору более качественную информацию о типе и при возможности выбора между использованием ссылки на существующий метод и использованием лямбда-выражения, следует всегда предпочитать использование ссылки на метод. !!! из Лямбды нельзя изменять переменные содержащиеся в ее методе. трюк с массивой единичной длины !!!В Java 8 можно любой анонимный класс, который реализует один абстрактный метод заменить на лямбду выражение, если анонимный класс реализует два абстрактных метода, то заменить на лямбду будет невозможно. 3) Что такое Стрим. Стрим это последовательность элементов, потенциально бесконечная, свозможность применять к ней, сложные, поэтапные приобразования без цикла и условного оператора. И программа будет иметь, простую линейную структуру.(Владыкин) Стрим состаит из источника - (элементы) -> промежуточные операторв - (элементы) -> терминальный оператор !!! Важно обработка происходит от терминального источника Операторы можно разделить на две группы: Промежуточные или КОНВЕЕРНЫЕ операторы(“intermediate”, ещё называют “lazy”) — обрабатывают поступающие элементы и возвращают стрим. Промежуточных операторов в цепочке обработки элементов может быть много. Терминальные (“terminal”, ещё называют “eager”) — обрабатывают элементы и завершают работу стрима, так что терминальный оператор в цепочке может быть только один. У стрима может быть сколько угодно вызовов промежуточных операций и последним вызов конечной операции. При этом все промежуточные операции выполняются лениво и пока не будет вызвана конечная операция никаких действий на самом деле не происходит (похоже на создание объекта Thread или Runnable, без вызова start()). Стримы создаются на основе каких-либо источников, например классов из java.util.Collection. Операции над стримами могут выполняться как последовательно, так и параллельно. Потоки не могут быть использованы повторно. Как только была вызвана какая-нибудь конечная операция, поток закрывается. Ассоциативные массивы (maps), например, HashMap, не поддерживаются. Кроме универсальных объектных существуют особые виды стримов для работы с примитивными типами данных int, long и double: IntStream, LongStream и DoubleStream. Эти примитивные стримы работают так же, как и обычные объектные, но со следующими отличиями: используют специализированные лямбда-выражения, например, IntFunction или IntPredicate вместо Function и Predicate; поддерживают дополнительные конечные операции sum(), average(), mapToObj(). 4) Какие существуют способы создания стрима? 1) Из коллекции: Stream Стрим из List: list.stream() Стрим из Map: map.entrySet().stream() 2) Из набора значений: Stream 3) Из массива: Stream 4) Из файла (каждая строка в файле будет отдельным элементом в стриме): Stream 5) Из строки: IntStream fromString = "0123456789".chars(); 6) С помощью Stream.builder(): Stream 7) С помощью Stream.iterate() (бесконечный): Stream 8) С помощью Stream.generate() (бесконечный): Stream 9) Создание паралельного стрима collection.parallelStream() Stream 5) Какие бывают стримы (По разным критериям, например "конечные и бесконечные") Стримы бывают ПОСЛЕДОВАТЕЛЬНЫМИ (sequential) и ПАРАЛЕЛЬНЫМИ (parallel). Последовательные выполняются только в текущем потоке, а вот параллельные используют общий пул ForkJoinPool.commonPool(). При этом элементы разбиваются (если это возможно) на несколько групп и обрабатываются в каждом потоке отдельно. Затем на нужном этапе группы объединяются в одну для предоставления конечного результата. Чтобы получить параллельный стрим, нужно либо вызвать метод parallelStream() вместо stream(), либо превратить обычный стрим в параллельный, вызвав промежуточный оператор parallel. Кроме объектных стримов Stream - IntStream для int, - LongStream для long, - DoubleStream для double 6) Терминальные и промежуточные методы. ПРОМЕЖУТОЧНЫЕ ОПЕРАТОРЫ конвеерные filter Отфильтровывает записи, возвращает только записи, соответствующие условию collection.stream().filter(«a1»::equals).count() skip Позволяет пропустить N первых элементов collection.stream().skip(collection.size() — 1).findFirst().orElse(«1») distinct Возвращает стрим без дубликатов (для метода equals) collection.stream().distinct().collect(Collectors.toList()) map Преобразует каждый элемент стрима collection.stream().map((s) -> s + "_1").collect(Collectors.toList()) peek Возвращает тот же стрим, но применяет функцию к каждому элементу стрима collection.stream().map(String::toUpperCase).peek((e) -> System.out.print("," + e)). ollect(Collectors.toList()) limit Позволяет ограничить выборку определенным количеством первых элементов collection.stream().limit(2).collect(Collectors.toList()) sorted Позволяет сортировать значения либо в натуральном порядке, либо задавая Comparator collection.stream().sorted().collect(Collectors.toList()) mapToInt, mapToDouble, mapToLong Аналог map, но возвращает числовой стрим (то есть стрим из числовых примитивов) collection.stream().mapToInt((s) -> Integer.parseInt(s)).toArray() flatMap, flatMapToInt, flatMapToDouble, flatMapToLong Похоже на map, но может создавать из одного элемента несколько collection.stream().flatMap((p) -> Arrays.asList(p.split(",")).stream()).toArray(String[]::new) ТЕРМИНАЛЬНЫЕ ОПЕРАТОРЫ findFirst Возвращает первый элемент из стрима (возвращает Optional) collection.stream().findFirst().orElse(«1») findAny Возвращает любой подходящий элемент из стрима (возвращает Optional) collection.stream().findAny().orElse(«1») collect Представление результатов в виде коллекций и других структур данных collection.stream().filter((s) -> s.contains(«1»)).collect(Collectors.toList()) count Возвращает количество элементов в стриме collection.stream().filter(«a1»::equals).count() anyMatch Возвращает true, если условие выполняется хотя бы для одного элемента collection.stream().anyMatch(«a1»::equals) noneMatch Возвращает true, если условие не выполняется ни для одного элемента collection.stream().noneMatch(«a8»::equals) allMatch Возвращает true, если условие выполняется для всех элементов collection.stream().allMatch((s) -> s.contains(«1»)) min Возвращает минимальный элемент, в качестве условия использует компаратор collection.stream().min(String::compareTo).get() max Возвращает максимальный элемент, в качестве условия использует компаратор collection.stream().max(String::compareTo).get() forEach Применяет функцию к каждому объекту стрима, порядок при параллельном выполнении не гарантируется set.stream().forEach((p) -> p.append("_1")); forEachOrdered Применяет функцию к каждому объекту стрима, сохранение порядка элементов гарантирует list.stream().forEachOrdered((p) -> p.append("_new")); toArray Возвращает массив значений стрима collection.stream().map(String::toUpperCase).toArray(String[]::new); reduce Позволяет выполнять агрегатные функции на всей коллекцией и возвращать один результат collection.stream().reduce((s1, s2) -> s1 + s2).orElse(0) Обратите внимание методы findFirst, findAny, anyMatch это short-circuiting методы, то есть обход стримов организуется таким образом чтобы найти подходящий элемент максимально быстро, а не обходить весь изначальный стрим. Краткое описание терминальных методов работы со стримами findFirst Возвращает первый элемент из стрима (возвращает Optional) collection.stream().findFirst().orElse(«1») findAny Возвращает любой подходящий элемент из стрима (возвращает Optional) collection.stream().findAny().orElse(«1») collect Представление результатов в виде коллекций и других структур данных collection.stream().filter((s) -> s.contains(«1»)).collect(Collectors.toList()) 7) Способы получения стрима. 8) Может ли функциональный интерфейс содержать что-то кроме абстрактного метода? нет. Такое решение исходит из понятия функции. Формально функция имеет имя, список параметров и возвращаемое значение. Если функция имеет тоже имя и другой список параметров или другое возвращаемое значение, то это другая функция. Значит логично на одну функцию иметь один экземпляр интерфейса с одним методом (ведь функцию можно только применить, и ничего более). В языке Java нет функций, как независимых сущностей, поэтому их эмулируют путем использования интерфейсов с одним абстрактным методом (Single Abstract Method, SAM). 9) Может ли стрим использоваться повторно? Потоки в Java 8 не могут быть использованы повторно. Как только вы называете какую-нибудь терминальную операция, поток закрывается Чтобы избежать этого, мы должны создать новую цепь для каждой терминальной операции. Supplier () -> Stream.of("dd2", "aa2", "bb1", "bb3", "cc") .filter(s -> s.startsWith("a")); streamSupplier.get().anyMatch(s -> true); // операция пройдет успешно streamSupplier.get().noneMatch(s -> true); // здесь также все будет ok Каждый вызов конструктора get() создает новый поток, с которым мы можем безопасно работать. 10) Что такое функциональное программирование? Функциональное программирование — это парадигма декларативного программирования, в которой программы создаются путем последовательного применения функций, а не инструкций. Каждая из этих функций принимает входное значение и возвращает согласующееся с ним выходное значение, не изменяясь и не подвергаясь воздействию со стороны состояния программы. Для таких функций предусмотрено выполнение только одной операции, если же требуется реализовать сложный процесс, то используется уже композиция функций, связанных последовательно. В процессе ФП мы создаем код, состоящий из множества модулей, поскольку функции в нем могут повторно использоваться в разных частях программы путем вызова, передачи в качестве параметров или возвращения. 11) Какие бывают стримы? 1)последовательные и паралельные 12) Перечислить 3 терминальные операции. Их предназначение. 13) В чем разница между Comparable и Comparator? Comparable обеспечивает единую последовательность сортировки. Comparator предоставляет несколько последовательностей сортировки. Другими словами, мы можем отсортировать коллекцию на основе одного элемента, Другими словами, мы можем отсортировать коллекцию по нескольким элементам, таким как идентификатор, имя, цена и т. д. такого как идентификатор, имя и цена. Comparable влияет на исходный класс, т.е. модифицируется фактический класс. Comparator не влияет на исходный класс, т.е. фактический класс не изменяется. Comparable предоставляет метод compareTo() для сортировки элементов. Comparator предоставляет метод compare() для сортировки элементов. Comparable присутствует в пакете java.lang. Comparator присутствует в пакете java.util. Мы можем отсортировать элементы списка типа Comparable методом Collections.sort(List). Мы можем отсортировать элементы списка типа Comparator методом Collections.sort(List, Comparator). Comparable - имплементится самим классом, когда нужен натуральный порядок сортировки. Пример - класс String. Comparator - имплементится другими классами. Дает возможность отделить реализацию сравнения от класса и сделать несколько реализаций сравнения по разным параметрам для одного класса. 14)Что такое метод референс? Ссылки на метод Если лямбда выражения вызывают только один существующий метод, лучше ссылать на этот метод по его имени. Ссылки на методы (Method References) – это компактные лямбда выражения для методов у которых уже есть имя. Например: Ссылка на статический метод ContainingClass::staticMethodName Function System.out.println(function.apply("TRUE")); Перепишем с помощью ссылки: Function System.out.println(function.apply("TRUE")); Ссылка на нестатический метод конкретного объекта containingObject::instanceMethodName Этот тип используется когда лямбда выражение вызывает метод внешнего уже существующего объекта. Consumer consumer.accept("OCPJP 8"); Перепишем, используя ссылку: Consumer consumer.accept("OCPJP 8"); Еще один пример: Integer integer = 5; Supplier System.out.println(supplier.get()); Перепишем: Integer integer = 5; Supplier System.out.println(supplier.get()); Ссылка на нестатический метод любого объекта конкретного типа ContainingType::methodName Function System.out.println(function.apply("OCPJP 8")); Перепишем: Function System.out.println(function.apply("OCPJP 8")); Ссылка на конструктор ClassName::new ClassName не может быть абстрактным классом или интерфейсом. Function System.out.println(function.apply("4")); Перепишем: Function System.out.println(function.apply("4")); 15) Может ли лямбда-выражение быть в несколько строк? да может, они должны быть обернуты в скобки 16) Что такое стримы? Для чего они нужны? Когда их лучше использовать? Стримы избавляют программистов от написания стереотипного кода всякий раз, когда нужно сделать что-то с набором элементов. То есть благодаря стримам не приходится думать о деталях реализации. Есть и другие плюсы: Стримы поддерживают один из основных принципов хорошего проектирования — слабую связанность (low coupling). Чем меньше класс знает про другие классы — тем лучше. Алгоритму сортировки не должно быть важно, ч то конкретно он сортирует. Это и делают стримы. С помощью стримов операции с коллекциями проще распараллелить: в императивном подходе для этого бы понадобился минимум ещё один цикл. Стримы позволяют уменьшить число побочных эффектов: методы Stream API не меняют исходные коллекции. Со Stream API лаконично записываются сложные алгоритмы обработки данных. Stream API любит неизменяемые данные. Если вы хотите поменять существующие структуры данных, а не создать новые, вам нужно что-то другое. Посмотрите в сторону новых стандартных методов (например, List.replaceAll). Stream API любит независимые данные. Если для получения результата вам нужно использовать одновременно несколько элементов из входного набора, без сторонних библиотек будет очень коряво. Но библиотеки вроде StreamEx часто решают эту проблему. Stream API любит решать одну задачу за проход. Если вы хотите в один обход данных решить несколько разных задач, готовьтесь писать свои коллекторы. И не факт, что это вообще получится. Stream API не любит проверяемые исключения. Вам будет не очень удобно кидать их из операций Stream API. Опять же есть библиотеки, которые пытаются это облегчить (скажем, jOOλ), но я бы рекомендовал отказываться от проверяемых исключений. В стандартном Stream API не хватает некоторых операций, которые очень нужны. Например, takeWhile, появится только в Java 9. Может оказаться, что вы хотите чего-то вполне разумного и несложного, но сделать это не получится. Опять же, стоит заметить, что библиотеки вроде jOOλ и StreamEx решают большинство таких проблем. |