## Функциональный интерфейс **Функциональный интерфейс** - это интерфейс, содержащий единственный абстрактный метод (`single abstract method`). Может быть аннотирован как `@FunctionalInterface`. ```java public class Main { @FunctionalInterface public interface MyInterface { public double f(int a, int b); } MyInterface myInt = new myInterface() { @Override public double f(int a, int b) { return a * b; } }; System.out.println(myInt.f(11, 23)); } ``` ## Лямбда-выражение `([parameter]) -> lambda body` Однострочные: ```java () -> System.out.println("Lambdas are great"); ``` Блочные: ```java () -> { double pi = 3.1415; return pi; }; ``` ```java public static void main(String[] args) throws Exception { MyInterface myInt = (a,b) -> a * b; System.out.println(myInt.f(11, 23)); } ``` ```java @FunctionalInterface public interface MyInterface { public double f(double x); public boolean equals(Object obj); default public double g() { return 9.8; } } ``` Стандартные интерфейсы: - `Runnable: SAM - run()` - `Comparator: SAM - compare()` ## Параметризированный функциональный интерфейс ```java @FunctionalInterface public interface MyInterface { public T f(T t); } ``` Стандартный интерфейс: - `Predicate: SAM - test(T t)` ```java public class Main { static void f(List list, Predicate pr) { for (Integer n: list) if (pr.test(n)) System.out.println(n); } public static void main(String[] args) throws Exception { List list = Arrays.asList(1, 2, 3, 4 5, 6, 7, 8); f(list, (n) -> n % 2 == 0) } } ``` ## Ссылки на методы Если лямбда-выражения вызывают только один существующий метод, лучше ссылаться на этот метод по его имени. Ссылки на методы (Method References) - это компактные лямбда-выражения для методов, у которых уже есть имя. `object::instanceMethod` - ссылается на объектный метод предложенного объекта. `System.out::println` равно `x -> System.out.println(x)` `Class::staticMethod` - ссылается на статический метод класса `Math::max` равно `x -> System.out.println(x)` `Class:instanceMethod` - ссылается на объектный метод класса `String::length` равно `x -> x.length()` Ссылка на конструктор ## Stream API (`java.util.stream`) Коллекции обеспечивают эффективный доступ к одиночным объектам, а **стримы**, наоборот, для прямого доступа и обработки отдельных элементов не используются. **Стримы** предназначены для параллельных и последовательных агрегаций, выполняемых через цепочку методов. - Поток однотипных данных для однотипной обработки - Промежуточные операции описывают рецепт обработки, но ничего не делают - Вся работа производится при вызове терминальной операции #### Stream примитивных типов Чтобы обеспечить способ работы 3 наиболее часто используемыми типами примитивов - `int`, `long` и `double` - библиотека `java.util.stream` включает 3 реализации стрима примитивов: - `IntStream`: ```java public interface IntStream extends BaseStream ``` - `LongStream`: ```java public interface LongStream extends BaseStream ``` - `DoubleStream`: ```java public interface DoubleStream extends BaseStream ``` #### Примеры создания стримов Stream из `List`: ```java List list = new ArrayList<>(); list.stream(); ``` Stream из `Map`: ```java Map map = new HashMap<>(); map.entrySet().stream(); map.values().stream(); ``` Stream из массива, используя статический метод класса `Arrays` ```java String[] array = new String[10]; Arrays.stream(array); ``` #### Промежуточные операции - Провести операцию над каждым элементом - `peek()` - Преобразовать данные из одного типа в другой - `map()` - Отфильтровать элементы - `filter()` - Удалить дублирующиеся элементы - `distinct()` - Сортировка и обратная сортировка элементов - `sorted()` - Лимит количества элементов - `limit()` - Пропустить первые элементы - `skip()` #### Терминальные операции - Собрать элементы потока и преобразовать их к нужному типу - `collect()` - Итерация по каждому элементу - `forEach()` - Узнать количество элементов стрима - `count()` - Найти минимальное и максимальное значение - `min()` и `max()` `Comparator` удобно задать с помощью лямбда-функции: ```java Stream.of("a", "bb", "ccc").min((s1, s2) -> s1.length() - s2.length()).get(); Stream.of("a", "bb", "ccc").max(Comparator.comparingInt(String::length)).get(); ``` - Найти первый подходящий элемент - `findFirst()` - Найти любой подходящий элемент - `findAny()` - Все элементы соответствуют условию `allMatch()` - Все элементы НЕ соответствуют условию `noneMatch()` - Хотя бы один элемент соответствует условию `anyMatch()` - Сумма элементов стрима - `sum()` - Операция сведения к одному результату - `reduce()` #### Примеры ```java IntStream.of(120, 410, 85, 32, 314, 12) .filter(x -> x < 300) .map(x -> x + 11) .limit(3) .forEach(System.out::println); ``` ```java String a[] = ("One", "Two", "Three", "Four", "Five"); List list_ = Arrays.stream(a) .filter(s -> s.length() <= 4) .collect(Colletors.toList()); System.out.println(list_); ```