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

Что происходит с изначальными данными в Stream

2.0 Middle🔥 201 комментариев
#Stream API и функциональное программирование

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

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

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

Что происходит с изначальными данными в Stream

В Java Stream API исходные данные остаются неизменными. Streams работают на принципе функционального программирования, где операции над данными не модифицируют исходную коллекцию, а создают новые последовательности преобразованных элементов.

Ключевые принципы

Immutability (неизменяемость) — это фундаментальный принцип Stream API. Когда вы применяете операции к Stream, оригинальные данные остаются в первоначальном виде.

List<Integer> originalList = Arrays.asList(1, 2, 3, 4, 5);

// Stream операция не изменяет originalList
List<Integer> doubled = originalList.stream()
    .map(n -> n * 2)
    .collect(Collectors.toList());

System.out.println("Original: " + originalList);  // [1, 2, 3, 4, 5]
System.out.println("Doubled: " + doubled);        // [2, 4, 6, 8, 10]

Как именно это работает

Stream проходит через конвейер операций (pipeline):

  • Источник данных (source) — исходная коллекция
  • Промежуточные операции (intermediate) — map, filter, sorted и т.д.
  • Финальная операция (terminal) — collect, forEach, reduce и т.д.

Каждая промежуточная операция создаёт новый Stream, а не изменяет исходный. Финальная операция затем обрабатывает все элементы через цепочку преобразований.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Каждая операция создаёт новый Stream
Stream<String> stream1 = names.stream();                           // Исходный
Stream<String> stream2 = stream1.filter(n -> n.length() > 3);    // Новый Stream
Stream<String> stream3 = stream2.map(String::toUpperCase);       // Ещё новый Stream
List<String> result = stream3.collect(Collectors.toList());

// Исходный список не изменился
System.out.println(names); // [Alice, Bob, Charlie]

Исключения: операции, которые могут влиять на данные

Хотя Stream сам не изменяет источник, операции внутри Stream могут модифицировать объекты, если они mutable:

List<Person> people = Arrays.asList(
    new Person("Alice"),
    new Person("Bob")
);

// Осторожно! Это изменяет объекты в исходном списке
people.stream()
    .forEach(p -> p.setName(p.getName().toUpperCase()));

// Теперь люди в исходном списке имеют прописные имена!

Также Stream не копирует данные — он работает с ними по ссылке. Если вы используете операции вроде peek() для отладки, они выполняют действия над оригинальными объектами.

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

Streams используют ленивые вычисления (lazy evaluation). Промежуточные операции не выполняют работу, пока не будет вызвана финальная операция. Это означает, что если фильтр исключит элементы на начальном этапе, они не пройдут через последующие операции.

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

Stream<Integer> stream = numbers.stream()
    .filter(n -> {
        System.out.println("Filter: " + n);
        return n > 2;
    })
    .map(n -> {
        System.out.println("Map: " + n);
        return n * 2;
    });

// Ничего не напечатается! Финальная операция не вызвана
System.out.println("Stream created");

// Теперь выполнится весь конвейер
List<Integer> result = stream.collect(Collectors.toList());

Заключение

Stream API разработана с учётом функциональной парадигмы, где данные обрабатываются без побочных эффектов. Исходная коллекция остаётся нетронутой, что делает код более предсказуемым и безопасным для многопоточности. Это фундаментальное свойство позволяет разработчикам писать чистый, понятный и безопасный код.