1. Что такое дженерики
Скачать 0.84 Mb.
|
41. Какое худшее время работы метода get(key) для ключа, которого нет в HashMap? 42. Какое худшее время работы метода get(key) для ключа, который есть в HashMap? O(N). Худший случай - это поиск ключа в HashMap, вырожденного в список по причине совпадения ключей по hashCode() и для выяснения хранится ли элемент с определённым ключом может потребоваться перебор всего списка. 9. Функциональные интерфейсы 1. Что такое функциональный интерфейс? Примеры 2. Для чего нужна аннотация @FunctionalInterface? 3. Какие встроенные функциональные интерфейсы вы знаете? Функциональный интерфейс - это интерфейс, который определяет только один абстрактный метод. Основное назначение – использование в лямбда выражениях и method reference. Чтобы точно определить интерфейс как функциональный, добавлена аннотация @FunctionalInterface, работающая по принципу @Override. Она обозначит замысел и не даст определить второй абстрактный метод в интерфейсе. Интерфейс может включать сколько угодно default методов и при этом оставаться функциональным, потому что default методы - не абстрактные. встроенные функциональные интерфейсы: - Predicate - Consumer - Function - Supplier - UnaryOperator - BinaryOperator Predicate } public class LambdaApp { public static void main(String[] args) { Predicate System.out.println(isPositive.test(5)); // true System.out.println(isPositive.test(-7)); // false } } BinaryOperator BinaryOperator T apply(T t1, T t2); } import java.util.function.BinaryOperator; public class LambdaApp { public static void main(String[] args) { BinaryOperator System.out.println(multiply.apply(3, 5)); // 15 System.out.println(multiply.apply(10, -2)); // -20 } } UnaryOperator UnaryOperator T apply(T t); } import java.util.function.UnaryOperator; public class LambdaApp { public static void main(String[] args) { UnaryOperator System.out.println(square.apply(5)); // 25 } } Function R apply(T t); } import java.util.function.Function; public class LambdaApp { public static void main(String[] args) { Function + " долларов"; System.out.println(convert.apply(5)); // 5 долларов } } Consumer } import java.util.function.Consumer; public class LambdaApp { public static void main(String[] args) { Consumer } } Supplier T get(); } import java.util.Scanner; import java.util.function.Supplier; public class LambdaApp { public static void main(String[] args) { Supplier Scanner in = new Scanner(System.in); System.out.println("Введите имя: "); String name = in.nextLine(); return new User(name); }; User user1 = userFactory.get(); User user2 = userFactory.get(); System.out.println("Имя user1: " + user1.getName()); System.out.println("Имя user2: " + user2.getName()); } Введите имя: Том Введите имя: Сэм Имя user1: Том Имя user2: Сэм 4. Что такое ссылка на метод? Ссылки на методы (Method References) – это компактные лямбда выражения для методов, у которых уже есть имя. Ссылки на методы бывают четырех видов: - Ссылка на статический метод - ContainingClass::staticMethodName Function System.out.println(function.apply("TRUE")); Function System.out.println(function.apply("TRUE"));, - Ссылка на нестатический метод конкретного объекта - containingObject::instanceMethodName Consumer Consumer - Ссылка на нестатический метод любого объекта конкретного типа ContainingType::methodName Function System.out.println(function.apply("OCPJP 8")); Function System.out.println(function.apply("OCPJP 8")); - Ссылка на конструктор ClassName::new Function System.out.println(function.apply("4")); Function System.out.println(function.apply("4")); 5. Что такое лямбда-выражение? Чем его можно заменить? Представляет набор инструкций, которые можно выделить в отдельную переменную и затем многократно вызвать в различных местах программы. Образует реализацию метода, определенного в функциональном интерфейсе. При этом важно, что функциональный интерфейс должен содержать только один единственный метод без реализации. список параметров выражения -> тело лямбда-выражения (действия) Параметры лямбда-выражения должны соответствовать по типу параметрам метода из функционального интерфейса. в лямбда-выражении использование обобщений не допускается. В этом случае нам надо типизировать объект интерфейса определенным типом, который потом будет применяться в лямбда-выражении public class LambdaApp { public static void main(String[] args) { Operationable Operationable System.out.println(operation1.calculate(20, 10)); //30 System.out.println(operation2.calculate("20", "10")); //2010 } } interface Operationable T calculate(T x, T y); } Одним из ключевых моментов в использовании лямбд является отложенное выполнение (deferred execution). То есть мы определяем в одном месте программы лямбда-выражение и затем можем его вызывать при необходимости неопределенное количество раз в различных частях программы. Отложенное выполнение может потребоваться, к примеру, в следующих случаях: Выполнение кода отдельном потоке Выполнение одного и того же кода несколько раз Выполнение кода в результате какого-то события Выполнение кода только в том случае, когда он действительно необходим и если он необходим 10. Stream API 1. Что такое 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); - создаем экземпляр 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) (не возьмёт ни единого элемента из списка); - Экземпляр стрима нельзя использовать более одного раза; Коллекции Streams Конечны (хранят набор элементов) Бесконечны Индивидуальный доступ к элементам Нет индивид. доступа к элементам Можно менять (добавлять/удалять) элементы, в т.ч. через итератор Если как то обрабатываем данные, то не влияет на источник Кроме универсальных объектных существуют особые виды стримов для работы с примитивными типами данных int, long и double: IntStream, LongStream и DoubleStream. Эти примитивные стримы работают так же, как и обычные объектные, но со следующими отличиями: - используют специализированные лямбда-выражения, например, IntFunction или IntPredicate вместо Function и Predicate; - поддерживают дополнительные конечные операции sum(), average(), mapToObj() 2. Почему Stream называют ленивым? Ленивое программирование -- технология, которая позволяет вам отсрочить вычисление кода до тех пор, пока не понадобится его результирующее значение. Блок обработки – промежуточные операции не выполняются, пока не вызовется терминальная. 3. Какие существуют способы создания стрима? Из коллекции: Stream Из набора значений: Stream Из массива: Stream Из файла (каждая строка в файле будет отдельным элементом в стриме): Stream Из строки: IntStream fromString = "0123456789".chars(); С помощью Stream.builder(): Stream С помощью Stream.iterate() (бесконечный): Stream С помощью Stream.generate() (бесконечный): Stream 4. Как из коллекции создать стрим? Stream 5. Какие промежуточные методы в стримах вы знаете? 6. Расскажите про метод peek() “быстрый взгляд”. - peek (принимает Consumer) integerStream.peek(System.out::println) позволяет подсмотреть какие элементы летают на данном этапе с помощью System.out::println Stream 7. Расскажите про метод map() “маппинг – из одного в другое”. Отображение или маппинг позволяет задать функцию преобразования одного объекта в другой, то есть получить из элемента одного типа элемент другого типа. принимает Function. 8. Расскажите про метод 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 phoneStream = Stream.of(new Phone("iPhone 6 S", 54000), new Phone("Lumia 950", 45000), new Phone("Samsung Galaxy S 6", 40000)); 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)); 9. Чем отличаются методы map() и flatMap(). 10. Расскажите про метод filter() Метод filter() является промежуточной операцией принимающей предикат, который фильтрует все элементы, возвращая только те, что соответствуют условию. Stream 11. Расскажите про метод limit() limit(long n) применяется для выборки первых n элементов потоков. Этот метод также возвращает модифицированный поток, в котором не более n элементов. Stream "Nexus 7"); phoneStream.skip(1) .limit(2) .forEach(s->System.out.println(s)); // Lumia 950 Samsung Galaxy S 6 12. Расскажите про метод skip() skip(long n) используется для пропуска n элементов. Этот метод возвращает новый поток, в котором пропущены первые n элементов. 13. Расскажите про метод 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 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 { public int compare(Phone a, Phone b){ return a.getName().toUpperCase().compareTo(b.getName().toUpperCase()); } } 14. Расскажите про метод distinct() “особый” возвращает только ункальные элементы в виде потока Stream 15. Какие терминальные методы в стримах вы знаете? - forEach (принимает Consumer) например System.out::println (покажет все элементы, которые остались в стриме) - findFirst () первый в порядке следования элемент из стрима (возвращается OptionalInt, т.к. может вернуться 0) - allMatch (принимает предикат) позволяет удостовериться, удовлетворяют ли все элементы стрима определенному условию - min () возвращает минимальный элемент из стрима - count () возвращает количество элементов, оставшееся в стриме - sum () 0 - collect () собирает элементы стрима в новое хранилище, например список. Куда может собрать – смотри список Collectors: - groupingBy группирует по параметрам - averagingInt среднеарифметическое какого то параметра - summarizingInt дает кол-во элементов, суммму, мин, ср.арифм, макс значения - reduce () – позволяет вычислить свертку элементов стрима (результат применения некоторого бинарного оператора к паре элементов из стрима, пока от стрима не останется один единсттвенный элемент) свёртка — это математическая операция, применённая к двум функциям f и g, порождающая третью функцию, которая иногда может рассматриваться как модифицированная версия одной из первоначальных 16. Расскажите про метод collect() “собирать” собирает элементы стрима в новое хранилище, например список Эта функция представляет объект Collector, который определен в пакете java.util.stream. Java уже предоставляет ряд встроенных функций, определенных в классе Collectors: - toList(): преобразование к типу List - toSet(): преобразование к типу Set - toMap(): преобразование к типу Map - groupingBy() - разделяет коллекцию на несколько частей и возвращает Map - summingInt(), summingDouble(), summingLong() - возвращает сумму; List .filter(s->s.length()<10) .collect(Collectors.toList()); 17. Расскажите про метод reduce() “уменьшить” Позволяет выполнять какое-то действие на всей коллекции и возвращать один результат вычислим произведение набора чисел: Stream Optional System.out.println(result.get()); // 720 18. Расскажите про класс Collectors и его методы. В Java 8 в классе Collectors реализовано несколько распространённых коллекторов: - toList(), toCollection(), toSet() - представляют стрим в виде списка, коллекции или множества; - toConcurrentMap(), toMap() - позволяют преобразовать стрим в Map; - averagingInt(), averagingDouble(), averagingLong() - возвращают среднее значение; - summingInt(), summingDouble(), summingLong() - возвращает сумму; - summarizingInt(), summarizingDouble(), summarizingLong() - возвращают SummaryStatistics с разными агрегатными значениями; - -partitioningBy() - разделяет коллекцию на две части по соответствию условию и возвращает их как Map - groupingBy() - разделяет коллекцию на несколько частей и возвращает Map - mapping() - дополнительные преобразования значений для сложных Collector-ов. - Так же существует возможность создания собственного коллектора через Collector.of(): Collector ArrayList::new, List::add, (l1, l2) -> { l1.addAll(l2); return l1; } ); |