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

Что будет при применении 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 внутри объекта:

  1. Преобразует вложенную структуру в плоский поток
  2. Объединяет все элементы из всех списков в один Stream
  3. Функционален и читаем
  4. Эффективен для обработки данных
  5. Требует проверки на null для безопасности

Это один из самых мощных инструментов Stream API для работы со вложенными структурами данных.

Что будет при применении flatMap к списку Long внутри объекта? | PrepBro