Функциональное программирование: Lambda, Functional Interfaces и Higher-Order Functions
Функциональное программирование — это парадигма, которая рассматривает функции как основные строительные блоки кода. Она способствует использованию неизменяемых данных и чистых функций, что улучшает тестируемость и позволяет эффективнее распараллеливать вычисления.
Основы функционального программирования
Ключевые принципы
- Pure Functions: функции без побочных эффектов
- Immutability: неизменяемые структуры данных
- First-Class Functions: функции как значения
- Higher-Order Functions: функции, принимающие другие функции в качестве аргументов
- Function Composition: комбинирование функций
Чистые и нечистые функции
public class FunctionExamples {
// Чистая функция - никаких побочных эффектов
public static int add(int a, int b) {
return a + b;
// Всегда возвращает один и тот же результат для одних и тех же входных данных
// Нет побочных эффектов
}
// Нечистая функция - с побочными эффектами
private static int counter = 0;
public static int addToCounter(int value) {
counter += value; // Побочный эффект: изменяет глобальное состояние
return counter;
// Для одного и того же входного значения может вернуть разные результаты
}
// Чистая функция для обработки строк
public static String capitalize(String input) {
if (input == null) return null;
return input.toUpperCase();
}
// Нечистая функция с внешней зависимостью
public static String getCurrentTimeFormatted() {
return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
// Зависит от текущего времени
}
}
Lambda-выражения в Java
Синтаксис и основы
public class LambdaBasics {
// Традиционный подход с анонимным классом
public static void traditionalApproach() {
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Traditional approach");
}
};
runnable1.run();
}
// Lambda-выражение
public static void lambdaApproach() {
Runnable runnable2 = () -> System.out.println("Lambda approach");
runnable2.run();
}
// Lambda с разными синтаксическими вариантами
public static void lambdaSyntaxExamples() {
// Без параметров
Runnable noParams = () -> System.out.println("No parameters");
// Один параметр
Consumer<String> oneParam = s -> System.out.println("Parameter: " + s);
// Один параметр с явным типом
Consumer<String> oneParamTyped = (String s) -> System.out.println("Typed: " + s);
// Несколько параметров
BinaryOperator<Integer> twoParams = (a, b) -> a + b;
// Несколько параметров с типами
BinaryOperator<Integer> twoParamsTyped = (Integer a, Integer b) -> a + b;
// Несколько строк кода
Predicate<String> multiLine = s -> {
String trimmed = s.trim();
return trimmed.length() > 5 && trimmed.startsWith("A");
};
// Использование
noParams.run();
oneParam.accept("Hello");
System.out.println(twoParams.apply(5, 3));
System.out.println(multiLine.test("Hello World"));
}
}
Functional Interfaces
// Пользовательские Functional Interfaces
@FunctionalInterface
public interface StringProcessor {
String process(String input);
// Может содержать методы по умолчанию
default String processAndLog(String input) {
String result = process(input);
System.out.println("Processed: " + result);
return result;
}
// Может содержать статические методы
static StringProcessor toUpperCase() {
return String::toUpperCase;
}
}
@FunctionalInterface
public interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
// Использование Functional Interfaces
public class FunctionalInterfaceExamples {
public static void demonstrateInterfaces() {
// Predicate - логический тест
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<String> isLong = s -> s.length() > 10;
System.out.println("4 is even: " + isEven.test(4));
System.out.println("Hello is long: " + isLong.test("Hello"));
// Consumer - обрабатывает значения
Consumer<String> printer = System.out::println;
Consumer<List<Integer>> listPrinter = list -> list.forEach(System.out::println);
printer.accept("Hello World");
listPrinter.accept(Arrays.asList(1, 2, 3, 4, 5));
// Supplier - предоставляет значения
Supplier<Double> randomSupplier = Math::random;
Supplier<LocalDateTime> nowSupplier = LocalDateTime::now;
System.out.println("Random: " + randomSupplier.get());
System.out.println("Now: " + nowSupplier.get());
// Function - преобразование значений
Function<String, Integer> stringLength = String::length;
Function<Integer, String> intToString = Object::toString;
System.out.println("Length of Hello: " + stringLength.apply("Hello"));
System.out.println("String of 42: " + intToString.apply(42));
// UnaryOperator - специальный случай Function
UnaryOperator<String> upperCase = String::toUpperCase;
UnaryOperator<Integer> square = n -> n * n;
System.out.println("Upper: " + upperCase.apply("hello"));
System.out.println("Square: " + square.apply(5));
// BinaryOperator - два параметра
BinaryOperator<Integer> add = Integer::sum;
BinaryOperator<String> concat = String::concat;
System.out.println("Add: " + add.apply(3, 7));
System.out.println("Concat: " + concat.apply("Hello", " World"));
// Пользовательский Functional Interface
StringProcessor reverser = s -> new StringBuilder(s).reverse().toString();
StringProcessor prefixAdder = s -> "Prefix: " + s;
System.out.println("Reverse: " + reverser.process("Hello"));
System.out.println("Prefix: " + prefixAdder.process("World"));
// Пользовательский TriFunction
TriFunction<Integer, Integer, Integer, Integer> sumThree = (a, b, c) -> a + b + c;
System.out.println("Sum of 1,2,3: " + sumThree.apply(1, 2, 3));
}
}
Higher-Order Functions
Функции как параметры
public class HigherOrderFunctions {
// Функция, которая принимает функцию в качестве параметра
public static <T, R> List<R> map(List<T> list, Function<T, R> mapper) {
List<R> result = new ArrayList<>();
for (T item : list) {
result.add(mapper.apply(item));
}
return result;
}
// Функция, которая принимает предикат и фильтрует список
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
List<T> result = new ArrayList<>();
for (T item : list) {
if (predicate.test(item)) {
result.add(item);
}
}
return result;
}
// Функция, которая принимает функцию для редукции
public static <T> T reduce(List<T> list, T identity, BinaryOperator<T> accumulator) {
T result = identity;
for (T item : list) {
result = accumulator.apply(result, item);
}
return result;
}
// Функция, которая возвращает функцию (замыкание)
public static Function<Integer, Integer> createMultiplier(int multiplier) {
return number -> number * multiplier;
}
// Функция с несколькими функциями в качестве параметров
public static <T> List<T> processList(
List<T> list,
Predicate<T> filter,
Function<T, T> mapper,
Consumer<T> consumer) {
List<T> result = new ArrayList<>();
for (T item : list) {
if (filter.test(item)) {
T processed = mapper.apply(item);
consumer.accept(processed);
result.add(processed);
}
}
return result;
}
// Демонстрация
public static void demonstrateHigherOrderFunctions() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Map: возведение чисел в квадрат
List<Integer> squares = map(numbers, n -> n * n);
System.out.println("Squares: " + squares);
// Filter: только четные числа
List<Integer> evens = filter(numbers, n -> n % 2 == 0);
System.out.println("Evens: " + evens);
// Reduce: вычисление суммы
Integer sum = reduce(numbers, 0, Integer::sum);
System.out.println("Sum: " + sum);
// Замыкание: создание функции-удвоителя
Function<Integer, Integer> doubler = createMultiplier(2);
Function<Integer, Integer> tripler = createMultiplier(3);
System.out.println("Double of 5: " + doubler.apply(5));
System.out.println("Triple of 5: " + tripler.apply(5));
// Сложная обработка
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
List<String> processed = processList(
words,
s -> s.length() > 5, // Filter: только длинные слова
String::toUpperCase, // Map: в верхний регистр
System.out::println // Consumer: вывод
);
System.out.println("Processed: " + processed);
}
}
Композиция функций
public class FunctionComposition {
// Вспомогательные методы для композиции
public static <T, U, R> Function<T, R> compose(Function<U, R> f, Function<T, U> g) {
return x -> f.apply(g.apply(x));
}
public static <T> Function<T, T> compose(Function<T, T>... functions) {
return Arrays.stream(functions)
.reduce(Function.identity(), Function::andThen);
}
// Пример композиции
public static void demonstrateComposition() {
// Отдельные функции
Function<String, String> addPrefix = s -> "Hello " + s;
Function<String, String> addSuffix = s -> s + "!";
Function<String, String> toUpperCase = String::toUpperCase;
// Объединение функций
Function<String, String> greet = compose(addSuffix, addPrefix);
Function<String, String> greetLoud = compose(toUpperCase, greet);
System.out.println(greet.apply("World")); // Hello World!
System.out.println(greetLoud.apply("World")); // HELLO WORLD!
// С andThen (обратный порядок)
Function<String, String> greetAndThenUpper = addPrefix.andThen(toUpperCase).andThen(addSuffix);
System.out.println(greetAndThenUpper.apply("World")); // HELLO WORLD!
// Математические примеры
Function<Double, Double> multiplyBy2 = x -> x * 2;
Function<Double, Double> add3 = x -> x + 3;
Function<Double, Double> square = x -> x * x;
// (x * 2 + 3)²
Function<Double, Double> complexOperation = compose(square, compose(add3, multiplyBy2));
System.out.println("Complex operation (5): " + complexOperation.apply(5.0)); // ((5*2)+3)² = 13² = 169
}
}
Stream API и функциональное программирование
Обработка потоков
public class StreamFunctionalProgramming {
public static void demonstrateStreamProcessing() {
List<Person> people = Arrays.asList(
new Person("Alice", 25, "Engineering"),
new Person("Bob", 30, "Marketing"),
new Person("Charlie", 35, "Engineering"),
new Person("Diana", 28, "HR"),
new Person("Eve", 32, "Engineering")
);
// Сложная обработка потока
List<String> result = people.stream()
.filter(p -> p.getAge() >= 30) // Фильтр: возраст >= 30
.filter(p -> p.getDepartment().equals("Engineering")) // Фильтр: Engineering
.map(Person::getName) // Map: только имена
.map(String::toUpperCase) // Map: заглавные буквы
.sorted() // Сортировка
.collect(Collectors.toList()); // Сбор результата
System.out.println("Filtered and mapped: " + result);
// Редукция со специализированными операторами
double totalAge = people.stream()
.mapToInt(Person::getAge)
.sum();
System.out.println("Total age: " + totalAge);
// Группировка
Map<String, List<Person>> byDepartment = people.stream()
.collect(Collectors.groupingBy(Person::getDepartment));
System.out.println("By department: " + byDepartment);
// Разделение
Map<Boolean, List<Person>> byAge = people.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() >= 30));
System.out.println("By age >= 30: " + byAge);
// Пользовательский коллектор
Map<String, Double> avgAgeByDept = people.stream()
.collect(Collectors.groupingBy(
Person::getDepartment,
Collectors.averagingInt(Person::getAge)
));
System.out.println("Average age by department: " + avgAgeByDept);
}
// Класс Person для примеров
public static class Person {
private String name;
private int age;
private String department;
public Person(String name, int age, String department) {
this.name = name;
this.age = age;
this.department = department;
}
// Геттеры
public String getName() { return name; }
public int getAge() { return age; }
public String getDepartment() { return department; }
@Override
public String toString() {
return String.format("%s (%d, %s)", name, age, department);
}
}
}
Ленивые вычисления со Stream
public class LazyEvaluation {
public static void demonstrateLazyEvaluation() {
// Бесконечный поток, работающий благодаря ленивым вычислениям
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
// Вычисляются только первые 10 элементов
List<Integer> firstTen = infiniteStream
.limit(10)
.collect(Collectors.toList());
System.out.println("First 10: " + firstTen);
// Числа Фибоначчи с ленивыми вычислениями
Stream<Long> fibonacci = Stream.generate(new FibonacciSupplier());
List<Long> firstFibonacci = fibonacci
.limit(10)
.collect(Collectors.toList());
System.out.println("First 10 Fibonacci: " + firstFibonacci);
// Ленивые фильтр и map
List<Integer> processed = Stream.iterate(1, n -> n + 1)
.filter(LazyEvaluation::isPrime) // Только простые числа
.map(n -> n * n) // Возведение в квадрат
.limit(5) // Только первые 5
.collect(Collectors.toList());
System.out.println("First 5 prime squares: " + processed);
}
private static boolean isPrime(int n) {
if (n <= 1) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) return false;
}
return true;
}
// Поставщик Фибоначчи
private static class FibonacciSupplier implements Supplier<Long> {
private long a = 0;
private long b = 1;
@Override
public Long get() {
long result = a;
long next = a + b;
a = b;
b = next;
return result;
}
}
}
Функциональное программирование в Python
Lambda и функции высшего порядка
from functools import reduce, partial
from typing import List, Callable, Any
# Lambda выражения
def lambda_examples():
# Простые lambda функции
square = lambda x: x ** 2
add = lambda x, y: x + y
is_even = lambda x: x % 2 == 0
print(f"Square of 5: {square(5)}")
print(f"Add 3 + 7: {add(3, 7)}")
print(f"Is 4 even: {is_even(4)}")
# Lambda со встроенными функциями
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Map с lambda
squares = list(map(lambda x: x ** 2, numbers))
print(f"Squares: {squares}")
# Filter с lambda
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Evens: {evens}")
# Reduce с lambda
sum_all = reduce(lambda x, y: x + y, numbers)
print(f"Sum: {sum_all}")
# Сортировка с lambda
words = ["apple", "banana", "cherry", "date"]
sorted_by_length = sorted(words, key=lambda x: len(x))
print(f"Sorted by length: {sorted_by_length}")
# Функции высшего порядка
def higher_order_functions():
# Функция, принимающая функцию как параметр
def apply_operation(numbers: List[int], operation: Callable[[int], int]) -> List[int]:
return [operation(num) for num in numbers]
# Функция, возвращающая функцию
def create_multiplier(factor: int) -> Callable[[int], int]:
return lambda x: x * factor
# Использование
numbers = [1, 2, 3, 4, 5]
squares = apply_operation(numbers, lambda x: x ** 2)
cubes = apply_operation(numbers, lambda x: x ** 3)
print(f"Squares: {squares}")
print(f"Cubes: {cubes}")
# Замыкание
doubler = create_multiplier(2)
tripler = create_multiplier(3)
print(f"Double of 5: {doubler(5)}")
print(f"Triple of 5: {tripler(5)}")
# Композиция функций
def compose(f: Callable, g: Callable) -> Callable:
return lambda x: f(g(x))
add_one = lambda x: x + 1
multiply_by_two = lambda x: x * 2
add_then_multiply = compose(multiply_by_two, add_one)
multiply_then_add = compose(add_one, multiply_by_two)
print(f"Add then multiply (5): {add_then_multiply(5)}") # (5 + 1) * 2 = 12
print(f"Multiply then add (5): {multiply_then_add(5)}") # (5 * 2) + 1 = 11
# Частичное применение
def partial_functions():
def multiply(x: int, y: int) -> int:
return x * y
def power(base: int, exponent: int) -> int:
return base ** exponent
# Partial application
double = partial(multiply, 2)
triple = partial(multiply, 3)
square = partial(power, 2)
cube = partial(power, 3)
print(f"Double of 5: {double(5)}")
print(f"Triple of 5: {triple(5)}")
print(f"Square of 5: {square(5)}")
print(f"Cube of 5: {cube(5)}")
# Списковые выражения (функциональная альтернатива)
def list_comprehensions():
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Традиционный способ с map/filter
squares_even = list(filter(lambda x: x % 2 == 0, map(lambda x: x ** 2, numbers)))
# Со списковым выражением
squares_even_comp = [x ** 2 for x in numbers if x % 2 == 0]
print(f"Squares of evens (map/filter): {squares_even}")
print(f"Squares of evens (comprehension): {squares_even_comp}")
# Вложенные выражения
matrix = [[i * j for j in range(1, 4)] for i in range(1, 4)]
print(f"Multiplication table: {matrix}")
# Выражение для словаря
word_lengths = {word: len(word) for word in ["apple", "banana", "cherry"]}
print(f"Word lengths: {word_lengths}")
# Декораторы (функции высшего порядка)
def decorators():
def timing_decorator(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f} seconds")
return result
return wrapper
def memoization_decorator(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@timing_decorator
@memoization_decorator
def fibonacci(n: int) -> int:
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(f"Fibonacci(10): {fibonacci(10)}")
print(f"Fibonacci(15): {fibonacci(15)}") # Быстрее благодаря мемоизации
# Запуск всех примеров
if __name__ == "__main__":
print("=== Lambda Examples ===")
lambda_examples()
print("\n=== Higher-Order Functions ===")
higher_order_functions()
print("\n=== Partial Functions ===")
partial_functions()
print("\n=== List Comprehensions ===")
list_comprehensions()
print("\n=== Decorators ===")
decorators()
Неизменяемость и чистые функции
Неизменяемые структуры данных
public class ImmutableDataStructures {
// Неизменяемый класс Person
public static final class Person {
private final String name;
private final int age;
private final List<String> hobbies;
public Person(String name, int age, List<String> hobbies) {
this.name = Objects.requireNonNull(name);
this.age = age;
this.hobbies = List.copyOf(hobbies); // Defensive Copy
}
// Getter - без Setter!
public String getName() { return name; }
public int getAge() { return age; }
public List<String> getHobbies() { return hobbies; }
// Методы создают новые экземпляры
public Person withAge(int newAge) {
return new Person(this.name, newAge, this.hobbies);
}
public Person withName(String newName) {
return new Person(newName, this.age, this.hobbies);
}
public Person addHobby(String hobby) {
List<String> newHobbies = new ArrayList<>(this.hobbies);
newHobbies.add(hobby);
return new Person(this.name, this.age, newHobbies);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name) &&
Objects.equals(hobbies, person.hobbies);
}
@Override
public int hashCode() {
return Objects.hash(name, age, hobbies);
}
@Override
public String toString() {
return String.format("Person{name='%s', age=%d, hobbies=%s}", name, age, hobbies);
}
}
// Функциональные операции над неизменяемыми данными
public static void demonstrateImmutability() {
List<Person> people = Arrays.asList(
new Person("Alice", 25, Arrays.asList("reading", "swimming")),
new Person("Bob", 30, Arrays.asList("gaming", "cooking")),
new Person("Charlie", 35, Arrays.asList("music", "travel"))
);
// Исходный список остается неизменным
List<Person> olderPeople = people.stream()
.map(person -> person.withAge(person.getAge() + 1))
.collect(Collectors.toList());
System.out.println("Original: " + people);
System.out.println("Aged by 1: " + olderPeople);
// Функциональное преобразование
List<String> hobbies = people.stream()
.flatMap(person -> person.getHobbies().stream())
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println("All hobbies: " + hobbies);
}
}
Примеры чистых функций
public class PureFunctions {
// Чистая функция - без побочных эффектов
public static int calculateArea(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Dimensions must be positive");
}
return width * height;
}
// Чистая функция для обработки строк
public static String formatFullName(String firstName, String lastName) {
if (firstName == null || lastName == null) {
return "";
}
return String.format("%s %s",
firstName.trim().toUpperCase(),
lastName.trim().toUpperCase()
);
}
// Чистая функция для обработки списков
public static List<Integer> filterAndSquare(List<Integer> numbers, Predicate<Integer> predicate) {
return numbers.stream()
.filter(predicate)
.map(n -> n * n)
.collect(Collectors.toList());
}
// Нечистая функция - для сравнения
private static int counter = 0;
public static int incrementCounter() {
return counter++; // Побочный эффект!
}
// Демонстрация
public static void demonstratePureFunctions() {
// Чистая функция - всегда одинаковый результат
int area1 = calculateArea(5, 3);
int area2 = calculateArea(5, 3);
System.out.println("Areas are equal: " + (area1 == area2)); // true
// Чистая функция со строками
String fullName1 = formatFullName("John", "Doe");
String fullName2 = formatFullName("John", "Doe");
System.out.println("Names are equal: " + fullName1.equals(fullName2)); // true
// Чистая функция со списками
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenSquares = filterAndSquare(numbers, n -> n % 2 == 0);
System.out.println("Even squares: " + evenSquares);
// Нечистая функция - разные результаты
int count1 = incrementCounter();
int count2 = incrementCounter();
System.out.println("Counts are different: " + (count1 != count2)); // true
}
}
Монады и функциональные концепции
Optional Monad
public class MonadExamples {
// Optional для безопасных операций с null
public static void demonstrateOptional() {
Optional<String> optional = Optional.of("Hello World");
// Трансформация map
Optional<Integer> length = optional.map(String::length);
System.out.println("Length: " + length.orElse(0));
// Фильтрация
Optional<String> filtered = optional.filter(s -> s.length() > 5);
System.out.println("Filtered: " + filtered.orElse("Too short"));
// FlatMap для вложенных Optional
Optional<String> upperCase = optional.flatMap(s ->
s.length() > 5 ? Optional.of(s.toUpperCase()) : Optional.empty()
);
System.out.println("Upper case: " + upperCase.orElse("Not available"));
// Безопасная цепочка операций
String result = Optional.ofNullable(getUser())
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");
System.out.println("City: " + result);
}
// Either Monad (упрощенная реализация)
public static class Either<L, R> {
private final L left;
private final R right;
private Either(L left, R right) {
this.left = left;
this.right = right;
}
public static <L, R> Either<L, R> left(L value) {
return new Either<>(value, null);
}
public static <L, R> Either<L, R> right(R value) {
return new Either<>(null, value);
}
public boolean isLeft() { return left != null; }
public boolean isRight() { return right != null; }
public L getLeft() { return left; }
public R getRight() { return right; }
public <T> Either<L, T> map(Function<R, T> mapper) {
if (isRight()) {
return Either.right(mapper.apply(right));
}
return Either.left(left);
}
public <T> Either<L, T> flatMap(Function<R, Either<L, T>> mapper) {
if (isRight()) {
return mapper.apply(right);
}
return Either.left(left);
}
}
// Использование Either
public static Either<String, Integer> parseNumber(String input) {
try {
return Either.right(Integer.parseInt(input));
} catch (NumberFormatException e) {
return Either.left("Invalid number: " + input);
}
}
public static void demonstrateEither() {
Either<String, Integer> result1 = parseNumber("123");
Either<String, Integer> result2 = parseNumber("abc");
result1.map(n -> n * 2)
.map(Object::toString)
.ifRight(System.out::println)
.ifLeft(System.err::println);
result2.map(n -> n * 2)
.map(Object::toString)
.ifRight(System.out::println)
.ifLeft(System.err::println);
}
// Вспомогательные методы для Either
private static <T> void ifRight(Either<String, T> either, Consumer<T> consumer) {
if (either.isRight()) {
consumer.accept(either.getRight());
}
}
private static <T> void ifLeft(Either<String, T> either, Consumer<String> consumer) {
if (either.isLeft()) {
consumer.accept(either.getLeft());
}
}
// Вспомогательные классы для примеров
private static User getUser() {
return new User("John", new Address("123 Main St", "New York"));
}
private static class User {
private String name;
private Address address;
public User(String name, Address address) {
this.name = name;
this.address = address;
}
public Address getAddress() { return address; }
}
private static class Address {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
public String getCity() { return city; }
}
}
Производительность и лучшие практики
Производительность функционального подхода
public class FunctionalPerformance {
// Традиционный цикл
public static int traditionalSum(List<Integer> numbers) {
int sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
}
// Функциональный вариант со Stream
public static int functionalSum(List<Integer> numbers) {
return numbers.stream().reduce(0, Integer::sum);
}
// Parallel Stream для больших объёмов данных
public static int parallelSum(List<Integer> numbers) {
return numbers.parallelStream().reduce(0, Integer::sum);
}
// Сравнение производительности
public static void performanceComparison() {
List<Integer> numbers = IntStream.range(0, 1_000_000)
.boxed()
.collect(Collectors.toList());
// Прогрев
traditionalSum(numbers);
functionalSum(numbers);
parallelSum(numbers);
// Бенчмарк
long start = System.nanoTime();
int result1 = traditionalSum(numbers);
long traditionalTime = System.nanoTime() - start;
start = System.nanoTime();
int result2 = functionalSum(numbers);
long functionalTime = System.nanoTime() - start;
start = System.nanoTime();
int result3 = parallelSum(numbers);
long parallelTime = System.nanoTime() - start;
System.out.println("Results equal: " + (result1 == result2 && result2 == result3));
System.out.println("Traditional: " + (traditionalTime / 1_000_000) + " ms");
System.out.println("Functional: " + (functionalTime / 1_000_000) + " ms");
System.out.println("Parallel: " + (parallelTime / 1_000_000) + " ms");
}
// Лучшие практики
public static void bestPractices() {
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// 1. Method References вместо Lambda когда возможно
List<Integer> lengths1 = words.stream()
.map(s -> s.length()) // Lambda
.collect(Collectors.toList());
List<Integer> lengths2 = words.stream()
.map(String::length) // Method Reference
.collect(Collectors.toList());
// 2. Primitive Streams для оптимизации
int sum = words.stream()
.mapToInt(String::length) // IntStream вместо Stream<Integer>
.sum();
// 3. Использование ленивых вычислений
Optional<String> firstLong = words.stream()
.filter(s -> s.length() > 5)
.findFirst(); // Останавливается после первого совпадения
// 4. Предпочитайте неизменяемые операции
List<String> processed = words.stream()
.map(String::toUpperCase)
.filter(s -> s.startsWith("A"))
.collect(Collectors.toList()); // Новый список вместо модификации
System.out.println("Best practices completed");
}
}
Концепции, проверяемые на экзамене
Ключевые функциональные концепции
- Pure Functions: отсутствие побочных эффектов, детерминированность
- Immutability: неизменяемые структуры данных
- Higher-Order Functions: функции как параметры или возвращаемые значения
- Function Composition: комбинирование функций
- Lazy Evaluation: отложенное вычисление
- Monads: Optional, Either, Stream
Типичные экзаменационные задачи
- Реализуйте чистую функцию
- Объясните Function Composition
- Сравните императивный и функциональный подходы
- Реализуйте Higher-Order Function
- Опишите преимущества функционального программирования
Итоги
Функциональное программирование даёт множество преимуществ:
- Тестируемость: чистые функции легко проверять
- Параллелизируемость: отсутствие побочных эффектов упрощает параллельное программирование
- Поддерживаемость: неизменяемые данные снижают количество ошибок
- Читаемость: декларативный код часто более понятен
Сочетание объектно-ориентированного и функционального подходов позволяет создавать надёжные, масштабируемые и легко поддерживаемые архитектуры.
Рекомендуемая литература
Keine Bücher für Kategorie "programming-languages" gefunden.
Другие статьи по функциональному программированию
Функциональное программирование - это важная парадигма современной разработки. Следующие статьи помогут тебе освоить все аспекты функционального подхода.
Основы и концепции
- Функциональное программирование: Lambda выражения - Lambda-выражения и Functional Interfaces
- Функциональное программирование: Lambda со Streams - Stream API и функциональные операции

