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

Как вернуть List из Stream в Stream API

1.0 Junior🔥 231 комментариев
#Stream API и функциональное программирование#Коллекции

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

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

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

# Преобразование Stream в List в Java Stream API

Основной метод: collect()

Главное правило: Stream — это ленивая цепочка трансформаций. Чтобы получить результат, нужно вызвать terminal операцию. Для List используется collect().

1. Базовый способ — toList()

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

// Простой способ (Java 16+)
List<Integer> doubled = numbers.stream()
    .map(n -> n * 2)
    .toList();  // collect(Collectors.toList())

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

Что происходит:

  1. .stream() — создаёт Stream из List
  2. .map(n -> n * 2) — трансформирует каждый элемент
  3. .toList() — terminal операция, собирает результат в List

Результат: immutable List (unmodifiable)

2. Способ с Collectors.toList() (Java 8+)

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

List<Integer> doubled = numbers.stream()
    .map(n -> n * 2)
    .collect(Collectors.toList());

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

Отличие от .toList():

  • .toList() (Java 16+) возвращает immutable List
  • Collectors.toList() возвращает mutable List (обычно ArrayList)
// .toList() — immutable
List<Integer> list1 = numbers.stream().toList();
list1.add(100);  // ❌ UnsupportedOperationException

// Collectors.toList() — mutable
List<Integer> list2 = numbers.stream().collect(Collectors.toList());
list2.add(100);  // ✅ Работает

3. Способ с Collectors.toCollection()

// ArrayList
List<Integer> doubled = numbers.stream()
    .map(n -> n * 2)
    .collect(Collectors.toCollection(ArrayList::new));

// LinkedList
List<Integer> linked = numbers.stream()
    .map(n -> n * 2)
    .collect(Collectors.toCollection(LinkedList::new));

// Любая другая реализация List
List<Integer> custom = numbers.stream()
    .map(n -> n * 2)
    .collect(Collectors.toCollection(MyCustomList::new));

Практические примеры

Фильтр + Трансформация

List<User> users = Arrays.asList(
    new User(1, "Alice", 25),
    new User(2, "Bob", 17),
    new User(3, "Charlie", 30)
);

// Получить имена пользователей старше 18
List<String> adultNames = users.stream()
    .filter(user -> user.getAge() >= 18)
    .map(User::getName)
    .toList();

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

Вложенные Stream (flatMap)

public class Order {
    private List<Item> items;
    
    public List<Item> getItems() {
        return items;
    }
}

List<Order> orders = Arrays.asList(
    new Order(Arrays.asList(new Item("A"), new Item("B"))),
    new Order(Arrays.asList(new Item("C")))
);

// Получить все товары из всех заказов
List<Item> allItems = orders.stream()
    .flatMap(order -> order.getItems().stream())
    .toList();

System.out.println(allItems);  // [A, B, C]

Дублирование элементов

List<Integer> numbers = List.of(1, 2, 3);

// Удвоить каждый элемент
List<Integer> doubled = numbers.stream()
    .flatMap(n -> Stream.of(n, n))
    .toList();

System.out.println(doubled);  // [1, 1, 2, 2, 3, 3]

Удаление дубликатов

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

List<Integer> unique = numbers.stream()
    .distinct()
    .toList();

System.out.println(unique);  // [1, 2, 3]

Сортировка

List<String> words = Arrays.asList("banana", "apple", "cherry");

// По алфавиту
List<String> sorted = words.stream()
    .sorted()
    .toList();

System.out.println(sorted);  // [apple, banana, cherry]

// По длине (обратный порядок)
List<String> sortedByLength = words.stream()
    .sorted(Comparator.comparingInt(String::length).reversed())
    .toList();

System.out.println(sortedByLength);  // [banana, cherry, apple]

Условный сбор

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

// Чётные числа
List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .toList();

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

Маппинг объектов

public class User {
    private String name;
    private int age;
    
    // getters...
}

public record UserDTO(String name, int age) {}

List<User> users = Arrays.asList(
    new User("Alice", 25),
    new User("Bob", 30)
);

// Преобразовать User в UserDTO
List<UserDTO> dtos = users.stream()
    .map(user -> new UserDTO(user.getName(), user.getAge()))
    .toList();

System.out.println(dtos);  // [UserDTO[name=Alice, age=25], ...]

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

Цепочка операций

List<Integer> result = numbers.stream()
    .filter(n -> n > 0)           // Только положительные
    .map(n -> n * 2)              // Удвоить
    .filter(n -> n < 20)          // Меньше 20
    .distinct()                   // Удалить дубликаты
    .sorted()                     // Отсортировать
    .toList();

Группировка данных

List<String> words = Arrays.asList("apple", "apricot", "banana", "blueberry");

// Группировать по первой букве
Map<Character, List<String>> grouped = words.stream()
    .collect(Collectors.groupingBy(word -> word.charAt(0)));

System.out.println(grouped);
// {a=[apple, apricot], b=[banana, blueberry]}

// Если нужна List из этих групп
List<List<String>> groupsList = grouped.values()
    .stream()
    .toList();

Объединение (join)

List<String> words = Arrays.asList("hello", "world", "java");

// Объединить в одну строку
String joined = words.stream()
    .collect(Collectors.joining(", "));

System.out.println(joined);  // hello, world, java

// Если нужна List с одним элементом
List<String> listWithJoined = words.stream()
    .collect(Collectors.joining(", "))
    .lines()  // Вернёт Stream<String>
    .toList();  // Собрать в List

Производительность

Промежуточные операции (lazy)

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

// Эта цепочка НИ ЧТО не делает!
// Это просто определение цепочки
var stream = numbers.stream()
    .map(n -> {
        System.out.println("Mapping: " + n);
        return n * 2;
    })
    .filter(n -> {
        System.out.println("Filtering: " + n);
        return n > 4;
    });

// Печать начнётся только здесь (terminal операция)
List<Integer> result = stream.toList();

Оптимизация

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

// ❌ Неэффективно — несколько проходов
List<Integer> result = numbers.stream()
    .map(n -> n * 2)
    .toList();
long sum = numbers.stream()
    .map(n -> n * 2)
    .mapToLong(n -> n)
    .sum();

// ✅ Эффективно — один проход
var numbers2 = numbers.stream()
    .map(n -> n * 2)
    .toList();
long sum2 = numbers2.stream()
    .mapToLong(n -> (long) n)
    .sum();

Альтернативы collect()

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

// Получить массив
Integer[] array = numbers.stream()
    .toArray(Integer[]::new);

// Получить Set
Set<Integer> set = numbers.stream()
    .collect(Collectors.toSet());

// Получить Map
Map<Integer, String> map = numbers.stream()
    .collect(Collectors.toMap(
        n -> n,
        n -> "Number: " + n
    ));

// Получить Stream (если зачем-то нужно)
Stream<Integer> stream = numbers.stream()
    .map(n -> n * 2);

Заключение

  • .toList() (Java 16+) — самый простой способ, возвращает immutable List
  • .collect(Collectors.toList()) — универсальный способ, возвращает mutable List
  • .collect(Collectors.toCollection()) — если нужна конкретная реализация (ArrayList, LinkedList)
  • Stream API — это functional programming подход, мощный инструмент для трансформации данных
  • Помни про lazy evaluation — операции выполняются только при вызове terminal операции (toList, collect, etc.)