Блок Примитивные типы
Скачать 6.67 Mb.
|
Какие бывают стримы? 1) конечные и бесконечные; Бесконечные (iterate, generate) - создают бесконечный поток данных, до срабатывания условия. 2) последовательные и параллельные; Стримы бывают последовательными (sequential) и параллельными (parallel). Последовательные выполняются только в текущем потоке, а вот параллельные используют общий пул ForkJoinPool.commonPool(). При этом элементы разбиваются (если это возможно) на несколько групп и обрабатываются в каждом потоке отдельно. Затем на нужном этапе группы объединяются в одну для предоставления конечного результата. Распараллеливание потоков позволяет задействовать несколько ядер процессора (если целевая машина многоядерная) и тем самым может повысить производительность и ускорить вычисления. В то же время говорить, что применение параллельных потоков на многоядерных машинах однозначно повысит производительность - не совсем корректно. В каждом конкретном случае надо проверять и тестировать. Чтобы получить параллельный стрим, нужно либо вызвать метод parallelStream() вместо stream(), либо превратить обычный стрим в параллельный, вызвав промежуточный оператор parallel. 2) объектные и примитивные; Специальные типы стримов для примитивных типов: - LongStream(), - DoubleStream(), - IntStream(). Что такое ленивая инициализация стрима? Ленивая инициализация — это концепция отсрочки создания объекта до тех пор, пока объект не будет фактически впервые использован. При правильном использовании это может привести к значительному повышению производительности. Все промежуточные операции выполняются только тогда, когда есть терминальная операция. Что возвращают промежуточные операции над стримом? Промежуточные операции возвращают новый трансформированный поток. К возвращенному потоку также можно применить ряд промежуточных операций, либо терминальную операцию. Что такое терминальная операция? Конечные или терминальные операции (операции сведения) возвращают конкретный результат. После этого никаких промежуточных операций применять нельзя. Две терминальные операции в одном выражении? Нет. В каком пакете находится Stream? Вся основная функциональность данного API сосредоточена в пакете java. util. stream. Чем Stream отличается от итератора? Порядок в обходе от итератора может быть задан и заранее определен. Стоимость (затраты мощностей процессора) доступа к элементам в стримах гораздо ниже. Протокол Iterator принципиально менее эффективен. Для получения каждого элемента требуется вызов двух методов. Кроме того, поскольку итераторы должны быть устойчивы к таким вещам, как вызов next() без hasNext() или hasNext() несколько раз без next(), оба этих метода обычно должны выполнять некоторую защитную кодировку ( и, как правило, больше состояния и ветвления), что увеличивает неэффективность. Сравнение стримов с коллекцией? Разница между коллекцией (Collection) данных и потоком (Stream) из новой JDK8 в том, что коллекции позволяют работать с элементами по-отдельности, тогда как поток (Stream) не позволяет. Например, с использованием коллекций, вы можете добавлять элементы, удалять, и вставлять в середину. Коллекции позволяют работать с элементами по отдельности, тогда как стримы так делать не позволяют, но вместо этого предоставляют возможность выполнять функции над данными как над одним целым. Также стоит отметить важность самой концепции сущностей: Collection - это прежде всего воплощение Структуры Данных. Например, Set не просто хранит в себе элементы, он реализует идею множества с уникальными элементами, тогда как Stream, это прежде всего абстракция необходимая для реализации конвейера вычислений, собственно поэтому, результатом работы конвейера являются те или иные Структуры Данных или же результаты проверок/поиска и т.п. Также основными отличиями Стримов от Коллекций являются: Поток данных не сохраняет своих элементов. Они могут храниться в основной коллекции или формироваться по требованию. Потоковые операции не изменяют источник данных Потоковые операции выполняются по требованию, когда это возможно или необходимо. Из каких частей состоит использование стримов? - создание стрима; - промежуточные операции (или их отсутствие); - терминальная операция (запускает весь процесс вычисления); Откуда можно получить стрим? - из коллекций - из значений - из массива - из файла - из строки - с помощью Stream.builder() - бесконечный стрима с помощью Stream.iterate() - бесконечный стрима с помощью Stream.generate() - из набора значений (метод .of()) В каком случае нужно закрывать стрим? Только потоки, источником которых является канал ввода-вывода, например Files.lines(Path, Charset) должны быть закрыты. Остальные реализуют AutoClosable. Можно ли конкатенировать стримы? если да, то каким методом? Объединение с помощью метода .concat(stream1, stream2); Можно ли получить пустой стрим? Пустой стрим: Stream.empty() Как получить стрим из массива? Arrays.stream(массив) Что такое коллекторы? Коллекторы – это объекты, разработанные для какой-либо агрегации потоковых данных. Поток, пройдя через цепочку отображений, фильтров и других не терминальных операций, по итогу представляет из себя поток объектов. Их необходимо либо собрать воедино, либо выбрать какой-то один, а может быть, и вообще получить новый информационный объект на основе каких-то характеристик. В этой точке и могут быть применены коллекторы. В чем разница map и flatMap? Оба map и flatMap могут быть применены к Stream Методы peek и forEach - в чем разница? Оба применяют функцию к каждому элементу стрима, но peek возвращает стрим значений исходного стрима, к которым будет применена определенная функция при их изъятии из потока, а forEach возвращает другой объект. В чем разница между forEach и forEachOrdered? У forEach порядок при параллельном выполнении не гарантируется. forEachOrdered() применяет функцию к каждому объекту стрима с сохранением порядка элементов. Разница методов. list() и walk()? Files.list() возвращает массив файлов в указанной директории, Files.walk() возвращает поток файлов в указанной директории и субдиректориях. Что такое саплайер-поставщик? Интерфейс Supplier используется тогда, когда на вход не передаются значения, но необходимо вернуть результат. Как получить стрим диапазона чисел? IntStream.range(0, 10) .collect(Collectors.toList()); В чем разница методов range и rangeClosed? В range(1, 10) в диапазон не включено число 10, а в rangeClosed(1, 10) число 10 включено. Может ли стрим использоваться повторно? Нет, каждый стрим одноразовый. Для чего нужны параллельные стримы? Распараллеливание потоков позволяет задействовать несколько ядер процессора (если целевая машина многоядерная) и тем самым может повысить производительность и ускорить вычисления. В то же время говорить, что применение параллельных потоков на многоядерных машинах однозначно повысит производительность - не совсем корректно. В каждом конкретном случае надо проверять и тестировать. Чтобы сделать обычный последовательный поток параллельным, надо вызвать у объекта Stream метод parallel(). Кроме того, можно также использовать метод parallelStream() интерфейса Collection для создания параллельного потока из коллекции. В то же время если рабочая машина не является многоядерной, то поток будет выполняться как последовательный. Приведи пример терминальной и промежуточной операции над стримом? Stream Stream Stream Stream Stream Stream Stream Stream Stream Object[] toArray(): возвращает массив из элементов потока. Терминальная операция. boolean allMatch(Predicate super T> predicate):возвращает true, если все элементы потока удовлетворяют условию в предикате. Терминальная операция boolean anyMatch(Predicate super T> predicate):возвращает true, если хоть один элемент потока удовлетворяют условию в предикате. Терминальная операция boolean noneMatch(Predicate super T> predicate):возвращает true, если ни один из элементов в потоке не удовлетворяет условию в предикате. Терминальная операция Optional Optional long count(): возвращает количество элементов в потоке. Терминальная операция. Optional Optional void forEach(Consumer super T> action): для каждого элемента выполняется действие action. Терминальная операция. Что такое лямбда? Как взаимосвязаны лямбда и функциональный интерфейс? Лямбда-выражение или просто лямбда в Java — упрощённая запись анонимного класса, реализующего функциональный интерфейс. Лямбда-выражение представляет сокращенную запись анонимного класса. ShapeService rectangleService = new ShapeService() { @Override public double perimeter(double a, double b) { return 2 * (a + b); } }; Эволюция в лямбда начинается с того, что опускается конструктор анонимного класса и имя метода интерфейса. Так как метод единственный в интерфейсе, то и его имя можно не упоминать. Параметры метода отделяются от тела метода оператором «стрелка»: ShapeService rectangleService = (double a, double b) -> { return 2 * (a + b); }; Если тело метода состоит из одного оператора, то и фигурные скобки можно опустить. ShapeService rectangleService = (double a, double b) -> 2 * (a + b); Типы параметров метода также можно опустить, так как предполагается, что они известны из сигнатуры единственного абстрактного метода. ShapeService rectangleService = (a, b) -> 2 * (a + b); Применение лямбда-выражений становится возможным только для функциональных интерфейсов из-за единственного абстрактного метода, так как исчезает необходимость явно указывать имя метода. Может ли лямбда-выражение быть в несколько строк? Да. В данном случае тело после -> должно обрамляться фигурными скобками {}, каждая строчка заканчиваться точкой с запятой. Если лямбда-выражение возвращает результат, ключевое слово return также требуется. К каким переменным и как можно обращаться в теле лямбда-выражений? Изнутри лямбда-выражения можно не только обращаться ко всем «видимым» переменным, но и вызывать те методы, к которым есть доступ, а именно: - переменные внутри метода (эффективно-финальные), - static переменные класса, - переменные интерфейса (с которым лямбда работает). Что такое Method References (ссылка на метод)? Если лямбда выражения вызывают только один существующий метод, лучше ссылать на этот метод по его имени. Ссылки на методы (Method References) – это компактные лямбда выражения для методов, у которых уже есть имя. Например: Consumer можно переписать с помощью method references: Consumer В каком виде передается Method References (ссылка на метод)? - имя_класса:: имя_стат_метода (для статического метода); - объект_класса:: имя_метода (для метода экземпляра); - название_класса:: new (для конструктора). Анонимные классы, как создать, где применяются, особенно как создать экземпляр? Основная особенность - анонимный класс не имеет имени. Анонимный класс является подклассом существующего класса или реализации интерфейса, т.е. АК всегда от кого-то наследуется. Использование таких классов обычно обусловлено необходимостью однократного создания объекта класса, реализующего тот или иной абстрактный класс или интерфейс. Поскольку анонимный класс не имеет имени, он не может иметь явный конструктор. Также к анонимному классу невозможно обратиться извне объявляющего его выражения, за исключением неявного обращения посредством объектной ссылки на суперкласс или интерфейс. Анонимные классы никогда не могут быть статическими, либо абстрактными, и всегда являются конечными классами. Кроме того, каждое объявление анонимного класса уникально. Объявление такого класса выполняется одновременно с созданием его объекта посредством использования оператора new. Синтаксис: после оператора new вызывается конструктор класса (в случае обычного наследования) или указывается абстрактный класс или интерфейс, после которого следуют пустые круглые скобки (в случае реализации), затем в фигурных скобках описывается реализация. После фигурных скобок тела анонимного класса обязательно ставить точку с запятой. Можно ли заменить каждый анонимный класс выражением лямбда? Ответ - нет. Вы можете создать анонимный класс для не заключительных классов и интерфейсов. Не то же самое для лямбда-выражений. Они могут использоваться только там, где ожидается интерфейс SAM (функциональный интерфейс), т.е. интерфейсы только с одним абстрактным методом (до Java 8 каждый метод интерфейса был абстрактным, но поскольку интерфейсы Java 8 могут также иметь стандартные и статические методы, которые не являются абстрактными, поскольку они имеют реализацию). Итак, какие анонимные классы можно заменить выражением лямбда? Только анонимные классы, которые являются реализациями интерфейса SAM (например, Runnable, ActionListener, Comparator, Predicate), могут быть заменены выражением лямбда. DefaultConsumer не может быть лямбда-мишенью, потому что это даже не интерфейс. А как насчет потребителя? Несмотря на то, что Consumer является интерфейсом, он не является интерфейсом SAM, потому что он содержит более одного абстрактного метода, t и лямбда-мишень. |