← Назад к вопросам

Что такое лямбда в Stream API?

1.8 Middle🔥 151 комментариев
#SOLID и паттерны проектирования#Spring Boot и Spring Data

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое лямбда в Stream API?

Лямбда (Lambda expression) — это анонимная функция в Java, которая позволяет передавать функцию как параметр. В контексте Stream API лямбды используются для обработки элементов потока данных компактно и функционально.

История: почему нужны лямбды

До Java 8, чтобы передать логику обработки элементов, нужно было создавать анонимные классы:

// ❌ Java 7 - многовербально
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Анонимный класс для фильтрации
List<Integer> filtered = numbers.stream()
    .filter(new Predicate<Integer>() {
        @Override
        public boolean test(Integer n) {
            return n > 2;  // Только одна строчка логики!
        }
    })
    .collect(Collectors.toList());

✅ Java 8+ - лямбда

// Компактно и понятно
List<Integer> filtered = numbers.stream()
    .filter(n -> n > 2)  // Лямбда!
    .collect(Collectors.toList());

Синтаксис лямбды

// Общий вид:
(параметры) -> { тело }

// Примеры:
n -> n > 5                          // 1 параметр
(a, b) -> a + b                     // 2 параметра
() -> System.out.println("Hi")     // 0 параметров
x -> { System.out.println(x); }     // Многострочное тело

Правила

  • Параметры в скобках (даже для одного параметра скобки опциональны)
  • Стрелка -> разделяет параметры и тело
  • Если тело одна строка, скобки {} опциональны
  • Если несколько строк — нужны {} и return

Лямбды в Stream API

1. map() — преобразование элементов

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Возвести в квадрат
List<Integer> squared = numbers.stream()
    .map(n -> n * n)  // Лямбда: (Integer) -> Integer
    .collect(Collectors.toList());

System.out.println(squared);  // [1, 4, 9, 16, 25]

// Преобразование в другой тип
List<String> strings = numbers.stream()
    .map(n -> "Number: " + n)  // (Integer) -> String
    .collect(Collectors.toList());

System.out.println(strings);
// [Number: 1, Number: 2, Number: 3, Number: 4, Number: 5]

2. filter() — фильтрация элементов

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// Оставить только чётные
List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)  // (Integer) -> boolean
    .collect(Collectors.toList());

System.out.println(evens);  // [2, 4, 6, 8, 10]

// Можно цепировать несколько filter
List<Integer> result = numbers.stream()
    .filter(n -> n > 3)      // Больше 3
    .filter(n -> n % 2 == 0) // И чётные
    .collect(Collectors.toList());

System.out.println(result);  // [4, 6, 8, 10]

3. forEach() — обработка каждого элемента

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Вывести каждое имя
names.stream()
    .forEach(name -> System.out.println(name));

// Можно сделать то же с методом:
names.stream()
    .forEach(System.out::println);  // Method reference

// Многострочная лямбда
names.stream()
    .forEach(name -> {
        String upper = name.toUpperCase();
        System.out.println(upper);
    });

4. reduce() — объединение элементов

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Сумма всех элементов
int sum = numbers.stream()
    .reduce(0, (acc, n) -> acc + n);

System.out.println(sum);  // 15

// Умножение всех элементов
int product = numbers.stream()
    .reduce(1, (a, b) -> a * b);

System.out.println(product);  // 120

// Соединение строк
List<String> words = Arrays.asList("Hello", "World", "Java");
String result = words.stream()
    .reduce("", (s1, s2) -> s1 + " " + s2);

System.out.println(result);  // " Hello World Java"

5. sorted() — сортировка

List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);

// Сортировка по возрастанию
List<Integer> sorted = numbers.stream()
    .sorted((a, b) -> a - b)  // (Integer, Integer) -> int
    .collect(Collectors.toList());

System.out.println(sorted);  // [1, 2, 5, 8, 9]

// Сортировка по убыванию
List<Integer> descending = numbers.stream()
    .sorted((a, b) -> b - a)  // Обратный порядок
    .collect(Collectors.toList());

System.out.println(descending);  // [9, 8, 5, 2, 1]

// Сортировка объектов
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
List<String> sorted = names.stream()
    .sorted((a, b) -> a.compareTo(b))  // Алфавитный порядок
    .collect(Collectors.toList());

System.out.println(sorted);  // [Alice, Bob, Charlie]

Functional Interface (Функциональный интерфейс)

Лямбды работают с функциональными интерфейсами — интерфейсами, которые имеют ровно один абстрактный метод:

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);  // Один метод!
}

// Лямбда реализует этот метод
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4));  // true

Встроенные функциональные интерфейсы в Java:

// Predicate: test(T) -> boolean
Predicate<Integer> isPositive = n -> n > 0;

// Function: apply(T) -> R
Function<Integer, String> toString = n -> "Number: " + n;

// Consumer: accept(T) -> void
Consumer<String> print = s -> System.out.println(s);

// Supplier: get() -> T
Supplier<Integer> random = () -> new Random().nextInt();

// BiFunction: apply(T, U) -> R
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;

Типизация лямбды

// Компилятор выводит типы параметров из контекста
List<Integer> numbers = Arrays.asList(1, 2, 3);

// Компилятор знает, что n — Integer (из List<Integer>)
numbers.stream()
    .filter(n -> n > 2)  // n вывел как Integer
    .forEach(System.out::println);

// Можно явно указать типы (необязательно)
numbers.stream()
    .filter((Integer n) -> n > 2)  // Явно: Integer
    .forEach(System.out::println);

// Если нельзя вывести, нужно указать явно
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;  // OK
// Без контекста: (a, b) -> a + b  // ❌ Ошибка компилятора

Сложный пример

public class StreamLambdaExample {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("Alice", 25),
            new User("Bob", 30),
            new User("Charlie", 22),
            new User("Dave", 28)
        );

        // Поток: фильтр → преобразование → сортировка → вывод
        users.stream()
            .filter(u -> u.getAge() >= 25)  // Возраст >= 25
            .map(u -> u.getName().toUpperCase())  // Имена в верхнем регистре
            .sorted((a, b) -> a.compareTo(b))  // Алфавитный порядок
            .forEach(name -> System.out.println("Hello, " + name));

        // Вывод:
        // Hello, ALICE
        // Hello, BOB
        // Hello, DAVE
    }
}

static class User {
    String name;
    int age;
    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    String getName() { return name; }
    int getAge() { return age; }
}

Лямбды vs Методные ссылки (Method References)

List<String> names = Arrays.asList("Alice", "Bob");

// Лямбда
names.forEach(name -> System.out.println(name));

// Методная ссылка (более компактно)
names.forEach(System.out::println);

// Обе работают одинаково, но вторая читается лучше

Захват переменных (Variable Capture)

int multiplier = 2;

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = numbers.stream()
    .map(n -> n * multiplier)  // Лямбда использует multiplier
    .collect(Collectors.toList());

System.out.println(result);  // [2, 4, 6, 8, 10]

// ВАЖНО: переменная должна быть final или effectively final
// multiplier = 3;  // ❌ Ошибка! Нельзя менять после использования в лямбде

Параллельная обработка

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Последовательная обработка
numbers.stream()
    .map(n -> n * n)
    .forEach(System.out::println);

// Параллельная обработка (если операция дорогая)
numbers.parallelStream()
    .map(n -> expensiveComputation(n))  // Лямбда выполняется параллельно
    .forEach(System.out::println);

Заключение

Лямбды в Stream API — это мощный инструмент для функционального программирования в Java. Они делают код:

  • Компактнее (меньше кода)
  • Читабельнее (больше сосредоточения на логике)
  • Функциональнее (декларативный стиль вместо императивного)

Понимание лямбд критично для современной Java разработки, особенно при работе с Stream API.

Что такое лямбда в Stream API? | PrepBro