← Назад к вопросам
Чем отличается метод map() от flatMap() в Stream API?
2.0 Middle🔥 141 комментариев
#Stream API и функциональное программирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между map() и flatMap() в Stream API
map() и flatMap() — это два промежуточных операции в Stream API для преобразования данных. Они похожи, но решают разные задачи.
Сравнительная таблица
| Параметр | map() | flatMap() |
|---|---|---|
| Преобразование | один элемент → один элемент | один элемент → несколько элементов |
| Тип результата | Stream<T> | Stream<R> (объединение нескольких потоков) |
| Структура | Сохраняет структуру | "Развертывает" вложенные потоки |
| Использование | Трансформация | Сглаживание (flattening) |
| Количество выходов | 1:1 | 1:N |
Метод map()
map() — это метод для преобразования каждого элемента потока в новое значение (обычно другого типа).
public interface Stream<T> {
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
}
Каждый элемент преобразуется ровно в один элемент:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Преобразуем каждое число в его квадрат
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println(squares); // [1, 4, 9, 16, 25]
// Преобразуем числа в строки
List<String> strings = numbers.stream()
.map(n -> "число: " + n)
.collect(Collectors.toList());
System.out.println(strings);
// [число: 1, число: 2, число: 3, число: 4, число: 5]
Практический пример map()
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
public class Main {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Иван", 25),
new User("Мария", 30),
new User("Петр", 28)
);
// Преобразуем User → String (имена)
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
System.out.println(names); // [Иван, Мария, Петр]
// Преобразуем User → Integer (возраст)
List<Integer> ages = users.stream()
.map(User::getAge)
.collect(Collectors.toList());
System.out.println(ages); // [25, 30, 28]
}
}
Метод flatMap()
flatMap() — это метод для преобразования каждого элемента в поток элементов, а затем объединения всех потоков в один. Это "развертывание" вложенной структуры.
public interface Stream<T> {
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
}
Каждый элемент может быть преобразован в несколько элементов:
List<List<Integer>> matrix = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
// map() вернет Stream<List<Integer>>
List<List<Integer>> result1 = matrix.stream()
.map(list -> list) // нет преобразования
.collect(Collectors.toList());
System.out.println(result1); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// flatMap() развернет все списки в один поток
List<Integer> result2 = matrix.stream()
.flatMap(List::stream) // развертывание
.collect(Collectors.toList());
System.out.println(result2); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
Визуализация:
map():
Input: [[1, 2], [3, 4]]
↓
Output: [[1, 2], [3, 4]]
flatMap():
Input: [[1, 2], [3, 4]]
↓
Output: [1, 2, 3, 4]
Практические примеры
Пример 1: Распределение задач по отделам
public class Department {
private String name;
private List<String> tasks;
public Department(String name, List<String> tasks) {
this.name = name;
this.tasks = tasks;
}
public List<String> getTasks() { return tasks; }
}
public class Main {
public static void main(String[] args) {
List<Department> departments = Arrays.asList(
new Department("Frontend",
Arrays.asList("Верстка", "JavaScript", "Дизайн")),
new Department("Backend",
Arrays.asList("API", "Database", "Security")),
new Department("DevOps",
Arrays.asList("Deployment", "Monitoring", "Infrastructure"))
);
// map() вернет Stream<List<String>> (вложенная структура)
departments.stream()
.map(Department::getTasks)
.forEach(System.out::println);
// Output:
// [Верстка, JavaScript, Дизайн]
// [API, Database, Security]
// [Deployment, Monitoring, Infrastructure]
// flatMap() развернет всё в один список задач
List<String> allTasks = departments.stream()
.flatMap(dept -> dept.getTasks().stream())
.collect(Collectors.toList());
System.out.println(allTasks);
// [Верстка, JavaScript, Дизайн, API, Database, Security, ...]
}
}
Пример 2: Преобразование строк в слова
public class Main {
public static void main(String[] args) {
List<String> sentences = Arrays.asList(
"Hello world",
"Java Stream",
"FlatMap example"
);
// map() дает список списков
sentences.stream()
.map(s -> s.split(" "))
.forEach(arr -> System.out.println(Arrays.toString(arr)));
// Output:
// [Hello, world]
// [Java, Stream]
// [FlatMap, example]
// flatMap() дает один список всех слов
List<String> allWords = sentences.stream()
.flatMap(s -> Arrays.stream(s.split(" ")))
.collect(Collectors.toList());
System.out.println(allWords);
// [Hello, world, Java, Stream, FlatMap, example]
}
}
Пример 3: Работа с Optional
public class Main {
public static void main(String[] args) {
List<String> emails = Arrays.asList(
"john@example.com",
null,
"jane@example.com",
null,
"bob@example.com"
);
// map() вернет Stream<Optional<String>>
List<Optional<String>> result1 = emails.stream()
.map(email -> Optional.ofNullable(email))
.collect(Collectors.toList());
System.out.println(result1.size()); // 5 (включая Optional.empty())
// flatMap() развернет Optional в Stream и отфильтрует пусто
List<String> result2 = emails.stream()
.flatMap(email -> Optional.ofNullable(email).stream())
.collect(Collectors.toList());
System.out.println(result2.size()); // 3 (только валидные email)
System.out.println(result2);
// [john@example.com, jane@example.com, bob@example.com]
}
}
Сравнение в действии
List<List<Integer>> matrix = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
System.out.println("=== map() ===");
List<Integer> sum1 = matrix.stream()
.map(list -> list.stream()
.mapToInt(Integer::intValue)
.sum())
.collect(Collectors.toList());
System.out.println(sum1); // [3, 7, 11] (суммы подсписков)
System.out.println("=== flatMap() ===");
List<Integer> allElements = matrix.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(allElements); // [1, 2, 3, 4, 5, 6] (все элементы)
int total = matrix.stream()
.flatMap(List::stream)
.mapToInt(Integer::intValue)
.sum();
System.out.println(total); // 21 (сумма всех элементов)
Сложный пример: flatMap с фильтрацией
public class Order {
private int id;
private List<Item> items;
public Order(int id, List<Item> items) {
this.id = id;
this.items = items;
}
public List<Item> getItems() { return items; }
}
public class Item {
private String name;
private double price;
public Item(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() { return name; }
public double getPrice() { return price; }
}
public class Main {
public static void main(String[] args) {
List<Order> orders = Arrays.asList(
new Order(1, Arrays.asList(
new Item("Laptop", 1000),
new Item("Mouse", 30)
)),
new Order(2, Arrays.asList(
new Item("Book", 20),
new Item("Pen", 5)
)),
new Order(3, Arrays.asList(
new Item("Phone", 800),
new Item("Charger", 50)
))
);
// Получить все товары стоимостью > 100
List<String> expensiveItems = orders.stream()
.flatMap(order -> order.getItems().stream())
.filter(item -> item.getPrice() > 100)
.map(Item::getName)
.collect(Collectors.toList());
System.out.println(expensiveItems); // [Laptop, Phone]
}
}
Ключевые моменты
- map() преобразует элемент 1:1
- flatMap() преобразует элемент 1:N и объединяет результаты
- flatMap() полезен для развертывания вложенных структур
- flatMap() с Optional — отличный способ фильтровать null значения
- flatMap() часто медленнее, чем map(), так как создает дополнительные потоки