← Назад к вопросам
Что будет при применении flatMap к списку Long внутри объекта?
2.0 Middle🔥 111 комментариев
#Stream API и функциональное программирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Применение flatMap к списку Long внутри объекта
Это отличный вопрос о Stream API и его интеграции с объектами. Давайте разберём это подробно.
Базовая ситуация
public class Container {
private List<Long> ids; // Список Long значений
public List<Long> getIds() {
return ids;
}
}
List<Container> containers = new ArrayList<>();
containers.add(new Container(Arrays.asList(1L, 2L, 3L)));
containers.add(new Container(Arrays.asList(4L, 5L)));
containers.add(new Container(Arrays.asList(6L, 7L, 8L)));
Применение flatMap
// Извлечь все Long значения из всех контейнеров
List<Long> allIds = containers.stream()
.flatMap(container -> container.getIds().stream())
.collect(Collectors.toList());
// Результат: [1, 2, 3, 4, 5, 6, 7, 8]
Как работает flatMap
Шаг за шагом:
containers.stream() // Stream<Container>
.flatMap(container -> container.getIds().stream()) // Преобразует каждый Container в Stream<Long>
// Затем ОБЪЕДИНЯЕТ все эти потоки в один Stream<Long>
.collect(Collectors.toList()); // Собирает в List<Long>
Визуально:
До flatMap (вложенная структура):
Stream<Container>
├── Container 1
│ └── [1, 2, 3]
├── Container 2
│ └── [4, 5]
└── Container 3
└── [6, 7, 8]
После flatMap (плоский поток):
Stream<Long>
├── 1
├── 2
├── 3
├── 4
├── 5
├── 6
├── 7
└── 8
Детальный пример с комментариями
public class FlatMapExample {
static class DataContainer {
private String name;
private List<Long> values;
public DataContainer(String name, List<Long> values) {
this.name = name;
this.values = values;
}
public List<Long> getValues() { return values; }
public String getName() { return name; }
}
public static void main(String[] args) {
List<DataContainer> data = new ArrayList<>();
data.add(new DataContainer("A", Arrays.asList(10L, 20L, 30L)));
data.add(new DataContainer("B", Arrays.asList(40L, 50L)));
data.add(new DataContainer("C", Arrays.asList(60L, 70L, 80L)));
// Простая флатмакс
List<Long> allValues = data.stream()
.flatMap(container -> container.getValues().stream())
.collect(Collectors.toList());
System.out.println("All values: " + allValues);
// All values: [10, 20, 30, 40, 50, 60, 70, 80]
// Флатмакс с фильтром
List<Long> filtered = data.stream()
.flatMap(container -> container.getValues().stream())
.filter(value -> value > 30)
.collect(Collectors.toList());
System.out.println("Filtered: " + filtered);
// Filtered: [40, 50, 60, 70, 80]
// Флатмакс с трансформацией
List<Long> doubled = data.stream()
.flatMap(container -> container.getValues().stream())
.map(value -> value * 2)
.collect(Collectors.toList());
System.out.println("Doubled: " + doubled);
// Doubled: [20, 40, 60, 80, 100, 120, 140, 160]
}
}
Сравнение с альтернативными подходами
// Способ 1: flatMap (функциональный подход)
List<Long> result1 = containers.stream()
.flatMap(c -> c.getIds().stream())
.collect(Collectors.toList());
// Способ 2: forEach с добавлением (императивный подход)
List<Long> result2 = new ArrayList<>();
for (Container container : containers) {
result2.addAll(container.getIds());
}
// Способ 3: reduce (свертка потока)
List<Long> result3 = containers.stream()
.reduce(new ArrayList<>(),
(list, container) -> {
list.addAll(container.getIds());
return list;
},
(list1, list2) -> {
list1.addAll(list2);
return list1;
});
// Все три способа дают одинаковый результат
// flatMap - самый читаемый и рекомендуемый
Пример с null значениями
public class FlatMapWithNull {
static class Container {
private List<Long> ids;
public Container(List<Long> ids) {
this.ids = ids;
}
public List<Long> getIds() {
return ids; // Может быть null!
}
}
public static void main(String[] args) {
List<Container> containers = new ArrayList<>();
containers.add(new Container(Arrays.asList(1L, 2L)));
containers.add(new Container(null)); // null список!
containers.add(new Container(Arrays.asList(3L, 4L)));
// ❌ Это вызовет NullPointerException
// List<Long> result = containers.stream()
// .flatMap(c -> c.getIds().stream())
// .collect(Collectors.toList());
// ✅ Правильный подход - защита от null
List<Long> result = containers.stream()
.flatMap(c -> c.getIds() != null ? c.getIds().stream() : Stream.empty())
.collect(Collectors.toList());
System.out.println(result); // [1, 2, 3, 4]
}
}
Более сложный пример
public class ComplexFlatMapExample {
static class User {
String name;
List<Post> posts;
public User(String name, List<Post> posts) {
this.name = name;
this.posts = posts;
}
}
static class Post {
String title;
List<Long> commentIds;
public Post(String title, List<Long> commentIds) {
this.title = title;
this.commentIds = commentIds;
}
}
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Alice", Arrays.asList(
new Post("Java Tips", Arrays.asList(1L, 2L, 3L)),
new Post("Spring Guide", Arrays.asList(4L, 5L))
)),
new User("Bob", Arrays.asList(
new Post("Python Tricks", Arrays.asList(6L, 7L))
))
);
// Получить все ID комментариев из всех постов всех пользователей
List<Long> allCommentIds = users.stream()
.flatMap(user -> user.posts.stream()) // Преобразуем User -> Stream<Post>
.flatMap(post -> post.commentIds.stream()) // Преобразуем Post -> Stream<Long>
.collect(Collectors.toList());
System.out.println("All comment IDs: " + allCommentIds);
// All comment IDs: [1, 2, 3, 4, 5, 6, 7]
// Вложенный flatMap с группировкой
Map<String, Long> commentCountByUser = users.stream()
.collect(Collectors.toMap(
user -> user.name,
user -> user.posts.stream()
.flatMap(post -> post.commentIds.stream())
.count()
));
System.out.println("Comments per user: " + commentCountByUser);
// Comments per user: {Alice=5, Bob=2}
}
}
Специальные потоки для примитивов
public class PrimitiveFlatMap {
// Для Long используй LongStream
List<Container> containers = ...;
// Вариант 1: Через Stream<Long>
List<Long> result1 = containers.stream()
.flatMap(c -> c.getIds().stream())
.collect(Collectors.toList());
// Вариант 2: Через LongStream (более эффективно)
long[] result2Array = containers.stream()
.flatMapToLong(c -> c.getIds().stream().mapToLong(Long::longValue))
.toArray();
// LongStream более эффективен для больших объёмов данных
// так как избегает auto-boxing Long объектов
}
Производительность
public class PerformanceComparison {
public static void main(String[] args) {
int containerCount = 1000;
int itemsPerContainer = 100;
List<Container> containers = new ArrayList<>();
for (int i = 0; i < containerCount; i++) {
List<Long> ids = new ArrayList<>();
for (int j = 0; j < itemsPerContainer; j++) {
ids.add((long)(i * itemsPerContainer + j));
}
containers.add(new Container(ids));
}
// Способ 1: flatMap
long start1 = System.nanoTime();
List<Long> result1 = containers.stream()
.flatMap(c -> c.getIds().stream())
.collect(Collectors.toList());
long time1 = (System.nanoTime() - start1) / 1_000_000;
// Способ 2: AddAll в цикле
long start2 = System.nanoTime();
List<Long> result2 = new ArrayList<>();
for (Container c : containers) {
result2.addAll(c.getIds());
}
long time2 = (System.nanoTime() - start2) / 1_000_000;
System.out.println("flatMap: " + time1 + "ms");
System.out.println("addAll loop: " + time2 + "ms");
}
}
Итог
При применении flatMap к списку Long внутри объекта:
- Преобразует вложенную структуру в плоский поток
- Объединяет все элементы из всех списков в один Stream
- Функционален и читаем
- Эффективен для обработки данных
- Требует проверки на null для безопасности
Это один из самых мощных инструментов Stream API для работы со вложенными структурами данных.