Что такое лямбда в Stream API?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое лямбда в 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.