## Функциональный интерфейс
**Функциональный интерфейс** - это интерфейс, содержащий единственный абстрактный метод (`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 <T> {
	public T f(T t);
}
```
Стандартный интерфейс:
- `Predicate<T>: SAM - test(T t)`
```java
public class Main {
	static void f(List<Integer> list, Predicate<Integer> pr) {
		for (Integer n: list)
			if (pr.test(n))
				System.out.println(n);
	}
	
	public static void main(String[] args) throws Exception {
		List<Integer> 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<Integer, IntStream>
	```
- `LongStream`:
	```java
	public interface LongStream extends BaseStream<Long, LongStream>
	```
- `DoubleStream`:
	```java
	public interface DoubleStream extends BaseStream<Double, DoubleStream>
	```
#### Примеры создания стримов
Stream из `List`:
```java
List<String> list = new ArrayList<>();
list.stream();
```
Stream из `Map`:
```java
Map<String, String> 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<String> list_ = Arrays.stream(a)
	.filter(s -> s.length() <= 4)
	.collect(Colletors.toList());
System.out.println(list_);
```